aboutsummaryrefslogtreecommitdiff
path: root/sys/compat
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat')
-rw-r--r--sys/compat/freebsd32/freebsd32.h75
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c123
-rw-r--r--sys/compat/freebsd32/freebsd32_proto.h9
-rw-r--r--sys/compat/freebsd32/freebsd32_syscall.h5
-rw-r--r--sys/compat/freebsd32/freebsd32_syscalls.c3
-rw-r--r--sys/compat/freebsd32/freebsd32_sysent.c3
-rw-r--r--sys/compat/freebsd32/freebsd32_systrace_args.c106
-rw-r--r--sys/compat/lindebugfs/lindebugfs.c86
-rw-r--r--sys/compat/linux/linux_file.c217
-rw-r--r--sys/compat/linux/linux_if.c11
-rw-r--r--sys/compat/linux/linux_ioctl.c120
-rw-r--r--sys/compat/linux/linux_ioctl.h29
-rw-r--r--sys/compat/linux/linux_misc.c1
-rw-r--r--sys/compat/linux/linux_netlink.c1
-rw-r--r--sys/compat/linux/linux_timer.c4
-rw-r--r--sys/compat/linuxkpi/common/include/asm/unaligned.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/acpi.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bcm47xx_nvram.h21
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bcma/bcma.h29
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bcma/bcma_regs.h17
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bitops.h24
-rw-r--r--sys/compat/linuxkpi/common/include/linux/cec-funcs.h16
-rw-r--r--sys/compat/linuxkpi/common/include/linux/cec.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/cgroup.h9
-rw-r--r--sys/compat/linuxkpi/common/include/linux/cleanup.h127
-rw-r--r--sys/compat/linuxkpi/common/include/linux/compiler.h48
-rw-r--r--sys/compat/linuxkpi/common/include/linux/compiler_attributes.h49
-rw-r--r--sys/compat/linuxkpi/common/include/linux/compiler_types.h45
-rw-r--r--sys/compat/linuxkpi/common/include/linux/dma-mapping.h21
-rw-r--r--sys/compat/linuxkpi/common/include/linux/eventfd.h54
-rw-r--r--sys/compat/linuxkpi/common/include/linux/file.h5
-rw-r--r--sys/compat/linuxkpi/common/include/linux/font.h33
-rw-r--r--sys/compat/linuxkpi/common/include/linux/fs.h34
-rw-r--r--sys/compat/linuxkpi/common/include/linux/hardirq.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/highmem.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/i2c.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ieee80211.h40
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kfifo.h22
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kmsg_dump.h51
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kstrtox.h57
-rw-r--r--sys/compat/linuxkpi/common/include/linux/memcontrol.h43
-rw-r--r--sys/compat/linuxkpi/common/include/linux/minmax.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mod_devicetable.h6
-rw-r--r--sys/compat/linuxkpi/common/include/linux/module.h18
-rw-r--r--sys/compat/linuxkpi/common/include/linux/netdevice.h24
-rw-r--r--sys/compat/linuxkpi/common/include/linux/notifier.h5
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pci.h59
-rw-r--r--sys/compat/linuxkpi/common/include/linux/platform_data/brcmfmac.h185
-rw-r--r--sys/compat/linuxkpi/common/include/linux/printk.h10
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/radix-tree.h35
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ratelimit.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/regulator/consumer.h17
-rw-r--r--sys/compat/linuxkpi/common/include/linux/seq_buf.h73
-rw-r--r--sys/compat/linuxkpi/common/include/linux/seq_file.h4
-rw-r--r--sys/compat/linuxkpi/common/include/linux/siphash.h168
-rw-r--r--sys/compat/linuxkpi/common/include/linux/skbuff.h9
-rw-r--r--sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h48
-rw-r--r--sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h14
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sort.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/spinlock.h31
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ssb/ssb_regs.h25
-rw-r--r--sys/compat/linuxkpi/common/include/linux/string_choices.h26
-rw-r--r--sys/compat/linuxkpi/common/include/linux/suspend.h6
-rw-r--r--sys/compat/linuxkpi/common/include/linux/swap.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sysfs.h34
-rw-r--r--sys/compat/linuxkpi/common/include/media/cec-notifier.h17
-rw-r--r--sys/compat/linuxkpi/common/include/media/cec.h23
-rw-r--r--sys/compat/linuxkpi/common/include/net/cfg80211.h20
-rw-r--r--sys/compat/linuxkpi/common/include/net/mac80211.h18
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.c1346
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.h13
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211_macops.c80
-rw-r--r--sys/compat/linuxkpi/common/src/linux_compat.c140
-rw-r--r--sys/compat/linuxkpi/common/src/linux_current.c10
-rw-r--r--sys/compat/linuxkpi/common/src/linux_eventfd.c63
-rw-r--r--sys/compat/linuxkpi/common/src/linux_firmware.c3
-rw-r--r--sys/compat/linuxkpi/common/src/linux_pci.c64
-rw-r--r--sys/compat/linuxkpi/common/src/linux_radix.c219
-rw-r--r--sys/compat/linuxkpi/common/src/linux_seq_buf.c64
-rw-r--r--sys/compat/linuxkpi/common/src/linux_seq_file.c14
-rw-r--r--sys/compat/linuxkpi/common/src/linux_shmemfs.c7
-rw-r--r--sys/compat/linuxkpi/common/src/linux_simple_attr.c48
-rw-r--r--sys/compat/linuxkpi/common/src/linux_siphash.c546
-rw-r--r--sys/compat/linuxkpi/common/src/linux_xarray.c4
-rw-r--r--sys/compat/linuxkpi/dummy/include/linux/fpu.h (renamed from sys/compat/linuxkpi/dummy/include/linux/eventfd.h)0
-rw-r--r--sys/compat/linuxkpi/dummy/include/linux/kdebug.h (renamed from sys/compat/linuxkpi/dummy/include/media/cec-notifier.h)0
-rw-r--r--sys/compat/linuxkpi/dummy/include/net/page_pool/helpers.h0
88 files changed, 3897 insertions, 1178 deletions
diff --git a/sys/compat/freebsd32/freebsd32.h b/sys/compat/freebsd32/freebsd32.h
index 9d724c93fee7..1436b630689f 100644
--- a/sys/compat/freebsd32/freebsd32.h
+++ b/sys/compat/freebsd32/freebsd32.h
@@ -30,20 +30,14 @@
#define _COMPAT_FREEBSD32_FREEBSD32_H_
#include <sys/abi_compat.h>
+#include <sys/devicestat.h>
+#include <sys/event.h>
+#include <sys/mount.h>
#include <sys/procfs.h>
#include <sys/socket.h>
#include <sys/user.h>
#include <sys/_ffcounter.h>
-/*
- * i386 is the only arch with a 32-bit time_t
- */
-#ifdef __amd64__
-typedef int32_t time32_t;
-#else
-typedef int64_t time32_t;
-#endif
-
struct timeval32 {
time32_t tv_sec;
int32_t tv_usec;
@@ -61,26 +55,24 @@ struct itimerspec32 {
struct bintime32 {
time32_t sec;
- uint32_t frac[2];
+ freebsd32_uint64_t frac;
};
struct ffclock_estimate32 {
struct bintime32 update_time;
- ffcounter update_ffcount;
- ffcounter leapsec_next;
- uint64_t period;
+ freebsd32_uint64_t update_ffcount;
+ freebsd32_uint64_t leapsec_next;
+ freebsd32_uint64_t period;
uint32_t errb_abs;
uint32_t errb_rate;
uint32_t status;
int16_t leapsec_total;
int8_t leapsec;
int8_t _pad;
-}
-#if defined(__amd64__)
-__attribute__((packed))
-#endif
-;
-#if defined(__amd64__)
+};
+_Static_assert(sizeof(ffcounter) == sizeof(freebsd32_uint64_t),
+ "'ffcounter' size discrepancy'");
+#if defined(__amd64__) || defined(__i386__)
_Static_assert(sizeof(struct ffclock_estimate32) == 52, "ffclock_estimate32 size");
#else
_Static_assert(sizeof(struct ffclock_estimate32) == 56, "ffclock_estimate32 size");
@@ -236,12 +228,12 @@ struct stat32 {
#endif
struct timespec32 st_birthtim;
off_t st_size;
- int64_t st_blocks;
+ freebsd32_uint64_t st_blocks;
uint32_t st_blksize;
uint32_t st_flags;
- uint64_t st_gen;
- uint64_t st_filerev;
- uint64_t st_spare[9];
+ freebsd32_uint64_t st_gen;
+ freebsd32_uint64_t st_filerev;
+ freebsd32_uint64_t st_spare[9];
};
struct freebsd11_stat32 {
uint32_t st_dev;
@@ -255,7 +247,7 @@ struct freebsd11_stat32 {
struct timespec32 st_mtim;
struct timespec32 st_ctim;
off_t st_size;
- int64_t st_blocks;
+ freebsd32_uint64_t st_blocks;
uint32_t st_blksize;
uint32_t st_flags;
uint32_t st_gen;
@@ -380,7 +372,7 @@ struct kinfo_proc32 {
u_int ki_slptime;
u_int ki_swtime;
u_int ki_cow;
- uint64_t ki_runtime;
+ freebsd32_uint64_t ki_runtime;
struct timeval32 ki_start;
struct timeval32 ki_childtime;
int ki_flag;
@@ -402,7 +394,7 @@ struct kinfo_proc32 {
char ki_moretdname[MAXCOMLEN-TDNAMLEN+1];
char ki_sparestrings[46];
int ki_spareints[KI_NSPARE_INT];
- uint64_t ki_tdev;
+ freebsd32_uint64_t ki_tdev;
int ki_oncpu;
int ki_lastcpu;
int ki_tracer;
@@ -419,6 +411,7 @@ struct kinfo_proc32 {
uint32_t ki_kstack;
uint32_t ki_udata;
uint32_t ki_tdaddr;
+ uint32_t ki_pd;
uint32_t ki_uerrmsg;
uint32_t ki_spareptrs[KI_NSPARE_PTR]; /* spare room for growth */
int ki_sparelongs[KI_NSPARE_LONG];
@@ -457,12 +450,12 @@ struct kinfo_knote32 {
union {
struct {
int knt_vnode_type;
- uint32_t knt_vnode_fsid[2];
- uint32_t knt_vnode_fileid[2];
+ freebsd32_uint64_t knt_vnode_fsid;
+ freebsd32_uint64_t knt_vnode_fileid;
char knt_vnode_fullpath[PATH_MAX];
} knt_vnode;
struct {
- uint32_t knt_pipe_ino[2];
+ freebsd32_uint64_t knt_pipe_ino;
} knt_pipe;
};
};
@@ -531,4 +524,28 @@ struct ptrace_sc_remote32 {
uint32_t pscr_args;
};
+struct devstat32 {
+ u_int sequence0;
+ int allocated;
+ u_int start_count;
+ u_int end_count;
+ struct bintime32 busy_from;
+ struct { u_int32_t stqe_next; } dev_links;
+ u_int32_t device_number;
+ char device_name[DEVSTAT_NAME_LEN];
+ int unit_number;
+ freebsd32_uint64_t bytes[DEVSTAT_N_TRANS_FLAGS];
+ freebsd32_uint64_t operations[DEVSTAT_N_TRANS_FLAGS];
+ struct bintime32 duration[DEVSTAT_N_TRANS_FLAGS];
+ struct bintime32 busy_time;
+ struct bintime32 creation_time;
+ u_int32_t block_size;
+ freebsd32_uint64_t tag_types[3];
+ devstat_support_flags flags;
+ devstat_type_flags device_type;
+ devstat_priority priority;
+ u_int32_t id;
+ u_int sequence1;
+};
+
#endif /* !_COMPAT_FREEBSD32_FREEBSD32_H_ */
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index 7913940338c2..4ec6dd452b32 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -203,6 +203,7 @@ void
freebsd32_rusage_out(const struct rusage *s, struct rusage32 *s32)
{
+ bzero(s32, sizeof(*s32));
TV_CP(*s, *s32, ru_utime);
TV_CP(*s, *s32, ru_stime);
CP(*s, *s32, ru_maxrss);
@@ -280,6 +281,37 @@ freebsd32_wait6(struct thread *td, struct freebsd32_wait6_args *uap)
return (error);
}
+int
+freebsd32_pdwait(struct thread *td, struct freebsd32_pdwait_args *uap)
+{
+ struct __wrusage32 wru32;
+ struct __wrusage wru, *wrup;
+ struct __siginfo32 si32;
+ struct __siginfo si, *sip;
+ int error, status;
+
+ wrup = uap->wrusage != NULL ? &wru : NULL;
+ if (uap->info != NULL) {
+ sip = &si;
+ bzero(sip, sizeof(*sip));
+ } else {
+ sip = NULL;
+ }
+ error = kern_pdwait(td, uap->fd, &status, uap->options, wrup, sip);
+ if (uap->status != NULL && error == 0)
+ error = copyout(&status, uap->status, sizeof(status));
+ if (uap->wrusage != NULL && error == 0) {
+ freebsd32_rusage_out(&wru.wru_self, &wru32.wru_self);
+ freebsd32_rusage_out(&wru.wru_children, &wru32.wru_children);
+ error = copyout(&wru32, uap->wrusage, sizeof(wru32));
+ }
+ if (uap->info != NULL && error == 0) {
+ siginfo_to_siginfo32 (&si, &si32);
+ error = copyout(&si32, uap->info, sizeof(si32));
+ }
+ return (error);
+}
+
#ifdef COMPAT_FREEBSD4
static void
copy_statfs(struct statfs *in, struct ostatfs32 *out)
@@ -681,31 +713,16 @@ freebsd32_pselect(struct thread *td, struct freebsd32_pselect_args *uap)
static void
freebsd32_kevent_to_kevent32(const struct kevent *kevp, struct kevent32 *ks32)
{
- uint64_t e;
int j;
CP(*kevp, *ks32, ident);
CP(*kevp, *ks32, filter);
CP(*kevp, *ks32, flags);
CP(*kevp, *ks32, fflags);
-#if BYTE_ORDER == LITTLE_ENDIAN
- ks32->data1 = kevp->data;
- ks32->data2 = kevp->data >> 32;
-#else
- ks32->data1 = kevp->data >> 32;
- ks32->data2 = kevp->data;
-#endif
+ FU64_CP(*kevp, *ks32, data);
PTROUT_CP(*kevp, *ks32, udata);
- for (j = 0; j < nitems(kevp->ext); j++) {
- e = kevp->ext[j];
-#if BYTE_ORDER == LITTLE_ENDIAN
- ks32->ext64[2 * j] = e;
- ks32->ext64[2 * j + 1] = e >> 32;
-#else
- ks32->ext64[2 * j] = e >> 32;
- ks32->ext64[2 * j + 1] = e;
-#endif
- }
+ for (j = 0; j < nitems(kevp->ext); j++)
+ FU64_CP(*kevp, *ks32, ext[j]);
}
void
@@ -722,38 +739,13 @@ freebsd32_kinfo_knote_to_32(const struct kinfo_knote *kin,
break;
case KNOTE_EXTDATA_VNODE:
CP(*kin, *kin32, knt_vnode.knt_vnode_type);
-#if BYTE_ORDER == LITTLE_ENDIAN
- kin32->knt_vnode.knt_vnode_fsid[0] = kin->knt_vnode.
- knt_vnode_fsid;
- kin32->knt_vnode.knt_vnode_fsid[1] = kin->knt_vnode.
- knt_vnode_fsid >> 32;
- kin32->knt_vnode.knt_vnode_fileid[0] = kin->knt_vnode.
- knt_vnode_fileid;
- kin32->knt_vnode.knt_vnode_fileid[1] = kin->knt_vnode.
- knt_vnode_fileid >> 32;
-#else
- kin32->knt_vnode.knt_vnode_fsid[1] = kin->knt_vnode.
- knt_vnode_fsid;
- kin32->knt_vnode.knt_vnode_fsid[0] = kin->knt_vnode.
- knt_vnode_fsid >> 32;
- kin32->knt_vnode.knt_vnode_fileid[1] = kin->knt_vnode.
- knt_vnode_fileid;
- kin32->knt_vnode.knt_vnode_fileid[0] = kin->knt_vnode.
- knt_vnode_fileid >> 32;
-#endif
+ FU64_CP(*kin, *kin32, knt_vnode.knt_vnode_fsid);
+ FU64_CP(*kin, *kin32, knt_vnode.knt_vnode_fileid);
memcpy(kin32->knt_vnode.knt_vnode_fullpath,
kin->knt_vnode.knt_vnode_fullpath, PATH_MAX);
break;
case KNOTE_EXTDATA_PIPE:
-#if BYTE_ORDER == LITTLE_ENDIAN
- kin32->knt_pipe.knt_pipe_ino[0] = kin->knt_pipe.knt_pipe_ino;
- kin32->knt_pipe.knt_pipe_ino[1] = kin->knt_pipe.
- knt_pipe_ino >> 32;
-#else
- kin32->knt_pipe.knt_pipe_ino[1] = kin->knt_pipe.knt_pipe_ino;
- kin32->knt_pipe.knt_pipe_ino[0] = kin->knt_pipe.
- knt_pipe_ino >> 32;
-#endif
+ FU64_CP(*kin, *kin32, knt_pipe.knt_pipe_ino);
break;
}
}
@@ -765,7 +757,7 @@ static int
freebsd32_kevent_copyout(void *arg, struct kevent *kevp, int count)
{
struct freebsd32_kevent_args *uap;
- struct kevent32 ks32[KQ_NEVENTS];
+ struct kevent32 ks32[KQ_NEVENTS] = {};
int i, error;
KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count));
@@ -787,7 +779,6 @@ freebsd32_kevent_copyin(void *arg, struct kevent *kevp, int count)
{
struct freebsd32_kevent_args *uap;
struct kevent32 ks32[KQ_NEVENTS];
- uint64_t e;
int i, j, error;
KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count));
@@ -803,20 +794,10 @@ freebsd32_kevent_copyin(void *arg, struct kevent *kevp, int count)
CP(ks32[i], kevp[i], filter);
CP(ks32[i], kevp[i], flags);
CP(ks32[i], kevp[i], fflags);
- kevp[i].data = PAIR32TO64(uint64_t, ks32[i].data);
+ FU64_CP(ks32[i], kevp[i], data);
PTRIN_CP(ks32[i], kevp[i], udata);
- for (j = 0; j < nitems(kevp->ext); j++) {
-#if BYTE_ORDER == LITTLE_ENDIAN
- e = ks32[i].ext64[2 * j + 1];
- e <<= 32;
- e += ks32[i].ext64[2 * j];
-#else
- e = ks32[i].ext64[2 * j];
- e <<= 32;
- e += ks32[i].ext64[2 * j + 1];
-#endif
- kevp[i].ext[j] = e;
- }
+ for (j = 0; j < nitems(kevp->ext); j++)
+ FU64_CP(ks32[i], kevp[i], ext[j]);
}
done:
return (error);
@@ -2306,11 +2287,11 @@ copy_stat(struct stat *in, struct stat32 *out)
TS_CP(*in, *out, st_mtim);
TS_CP(*in, *out, st_ctim);
CP(*in, *out, st_size);
- CP(*in, *out, st_blocks);
+ FU64_CP(*in, *out, st_blocks);
CP(*in, *out, st_blksize);
CP(*in, *out, st_flags);
- CP(*in, *out, st_gen);
- CP(*in, *out, st_filerev);
+ FU64_CP(*in, *out, st_gen);
+ FU64_CP(*in, *out, st_filerev);
CP(*in, *out, st_bsdflags);
TS_CP(*in, *out, st_birthtim);
out->st_padding1 = 0;
@@ -2519,7 +2500,7 @@ freebsd11_cvtstat32(struct stat *in, struct freebsd11_stat32 *out)
TS_CP(*in, *out, st_mtim);
TS_CP(*in, *out, st_ctim);
CP(*in, *out, st_size);
- CP(*in, *out, st_blocks);
+ FU64_CP(*in, *out, st_blocks);
CP(*in, *out, st_blksize);
CP(*in, *out, st_flags);
CP(*in, *out, st_gen);
@@ -4166,9 +4147,9 @@ freebsd32_ffclock_setestimate(struct thread *td,
CP(cest.update_time, cest32.update_time, sec);
memcpy(&cest.update_time.frac, &cest32.update_time.frac, sizeof(uint64_t));
- CP(cest, cest32, update_ffcount);
- CP(cest, cest32, leapsec_next);
- CP(cest, cest32, period);
+ FU64_CP(cest, cest32, update_ffcount);
+ FU64_CP(cest, cest32, leapsec_next);
+ FU64_CP(cest, cest32, period);
CP(cest, cest32, errb_abs);
CP(cest, cest32, errb_rate);
CP(cest, cest32, status);
@@ -4196,9 +4177,9 @@ freebsd32_ffclock_getestimate(struct thread *td,
CP(cest32.update_time, cest.update_time, sec);
memcpy(&cest32.update_time.frac, &cest.update_time.frac, sizeof(uint64_t));
- CP(cest32, cest, update_ffcount);
- CP(cest32, cest, leapsec_next);
- CP(cest32, cest, period);
+ FU64_CP(cest32, cest, update_ffcount);
+ FU64_CP(cest32, cest, leapsec_next);
+ FU64_CP(cest32, cest, period);
CP(cest32, cest, errb_abs);
CP(cest32, cest, errb_rate);
CP(cest32, cest, status);
diff --git a/sys/compat/freebsd32/freebsd32_proto.h b/sys/compat/freebsd32/freebsd32_proto.h
index 5c0efc64f8a7..12458ed4cc4d 100644
--- a/sys/compat/freebsd32/freebsd32_proto.h
+++ b/sys/compat/freebsd32/freebsd32_proto.h
@@ -699,6 +699,13 @@ struct freebsd32_setcred_args {
char wcred_l_[PADL_(const struct setcred32 *)]; const struct setcred32 * wcred; char wcred_r_[PADR_(const struct setcred32 *)];
char size_l_[PADL_(size_t)]; size_t size; char size_r_[PADR_(size_t)];
};
+struct freebsd32_pdwait_args {
+ char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
+ char status_l_[PADL_(int *)]; int * status; char status_r_[PADR_(int *)];
+ char options_l_[PADL_(int)]; int options; char options_r_[PADR_(int)];
+ char wrusage_l_[PADL_(struct __wrusage32 *)]; struct __wrusage32 * wrusage; char wrusage_r_[PADR_(struct __wrusage32 *)];
+ char info_l_[PADL_(struct __siginfo32 *)]; struct __siginfo32 * info; char info_r_[PADR_(struct __siginfo32 *)];
+};
int freebsd32_wait4(struct thread *, struct freebsd32_wait4_args *);
int freebsd32_ptrace(struct thread *, struct freebsd32_ptrace_args *);
int freebsd32_recvmsg(struct thread *, struct freebsd32_recvmsg_args *);
@@ -817,6 +824,7 @@ int freebsd32_aio_readv(struct thread *, struct freebsd32_aio_readv_args *);
int freebsd32_timerfd_gettime(struct thread *, struct freebsd32_timerfd_gettime_args *);
int freebsd32_timerfd_settime(struct thread *, struct freebsd32_timerfd_settime_args *);
int freebsd32_setcred(struct thread *, struct freebsd32_setcred_args *);
+int freebsd32_pdwait(struct thread *, struct freebsd32_pdwait_args *);
#ifdef COMPAT_43
@@ -1319,6 +1327,7 @@ int freebsd11_freebsd32_fstatat(struct thread *, struct freebsd11_freebsd32_fsta
#define FREEBSD32_SYS_AUE_freebsd32_timerfd_gettime AUE_TIMERFD
#define FREEBSD32_SYS_AUE_freebsd32_timerfd_settime AUE_TIMERFD
#define FREEBSD32_SYS_AUE_freebsd32_setcred AUE_SETCRED
+#define FREEBSD32_SYS_AUE_freebsd32_pdwait AUE_PDWAIT
#undef PAD_
#undef PADL_
diff --git a/sys/compat/freebsd32/freebsd32_syscall.h b/sys/compat/freebsd32/freebsd32_syscall.h
index f8ef7e4a20d3..9fed7ec58669 100644
--- a/sys/compat/freebsd32/freebsd32_syscall.h
+++ b/sys/compat/freebsd32/freebsd32_syscall.h
@@ -517,4 +517,7 @@
#define FREEBSD32_SYS_setgroups 596
#define FREEBSD32_SYS_jail_attach_jd 597
#define FREEBSD32_SYS_jail_remove_jd 598
-#define FREEBSD32_SYS_MAXSYSCALL 600
+#define FREEBSD32_SYS_pdrfork 600
+#define FREEBSD32_SYS_freebsd32_pdwait 601
+#define FREEBSD32_SYS_renameat2 602
+#define FREEBSD32_SYS_MAXSYSCALL 603
diff --git a/sys/compat/freebsd32/freebsd32_syscalls.c b/sys/compat/freebsd32/freebsd32_syscalls.c
index 645cdccbc02d..d91d45130c89 100644
--- a/sys/compat/freebsd32/freebsd32_syscalls.c
+++ b/sys/compat/freebsd32/freebsd32_syscalls.c
@@ -605,4 +605,7 @@ const char *freebsd32_syscallnames[] = {
"jail_attach_jd", /* 597 = jail_attach_jd */
"jail_remove_jd", /* 598 = jail_remove_jd */
"#599", /* 599 = kexec_load */
+ "pdrfork", /* 600 = pdrfork */
+ "freebsd32_pdwait", /* 601 = freebsd32_pdwait */
+ "renameat2", /* 602 = renameat2 */
};
diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c
index 240b54ae9011..68c0388eaa79 100644
--- a/sys/compat/freebsd32/freebsd32_sysent.c
+++ b/sys/compat/freebsd32/freebsd32_sysent.c
@@ -667,4 +667,7 @@ struct sysent freebsd32_sysent[] = {
{ .sy_narg = AS(jail_attach_jd_args), .sy_call = (sy_call_t *)sys_jail_attach_jd, .sy_auevent = AUE_JAIL_ATTACH, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 597 = jail_attach_jd */
{ .sy_narg = AS(jail_remove_jd_args), .sy_call = (sy_call_t *)sys_jail_remove_jd, .sy_auevent = AUE_JAIL_REMOVE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 598 = jail_remove_jd */
{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 599 = freebsd32_kexec_load */
+ { .sy_narg = AS(pdrfork_args), .sy_call = (sy_call_t *)sys_pdrfork, .sy_auevent = AUE_PDRFORK, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 600 = pdrfork */
+ { .sy_narg = AS(freebsd32_pdwait_args), .sy_call = (sy_call_t *)freebsd32_pdwait, .sy_auevent = AUE_PDWAIT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 601 = freebsd32_pdwait */
+ { .sy_narg = AS(renameat2_args), .sy_call = (sy_call_t *)sys_renameat2, .sy_auevent = AUE_RENAMEAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 602 = renameat2 */
};
diff --git a/sys/compat/freebsd32/freebsd32_systrace_args.c b/sys/compat/freebsd32/freebsd32_systrace_args.c
index 29a5497e9efa..e85cf4765c98 100644
--- a/sys/compat/freebsd32/freebsd32_systrace_args.c
+++ b/sys/compat/freebsd32/freebsd32_systrace_args.c
@@ -3427,6 +3427,37 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
*n_args = 1;
break;
}
+ /* pdrfork */
+ case 600: {
+ struct pdrfork_args *p = params;
+ uarg[a++] = (intptr_t)p->fdp; /* int * */
+ iarg[a++] = p->pdflags; /* int */
+ iarg[a++] = p->rfflags; /* int */
+ *n_args = 3;
+ break;
+ }
+ /* freebsd32_pdwait */
+ case 601: {
+ struct freebsd32_pdwait_args *p = params;
+ iarg[a++] = p->fd; /* int */
+ uarg[a++] = (intptr_t)p->status; /* int * */
+ iarg[a++] = p->options; /* int */
+ uarg[a++] = (intptr_t)p->wrusage; /* struct __wrusage32 * */
+ uarg[a++] = (intptr_t)p->info; /* struct __siginfo32 * */
+ *n_args = 5;
+ break;
+ }
+ /* renameat2 */
+ case 602: {
+ struct renameat2_args *p = params;
+ iarg[a++] = p->oldfd; /* int */
+ uarg[a++] = (intptr_t)p->old; /* const char * */
+ iarg[a++] = p->newfd; /* int */
+ uarg[a++] = (intptr_t)p->new; /* const char * */
+ iarg[a++] = p->flags; /* int */
+ *n_args = 5;
+ break;
+ }
default:
*n_args = 0;
break;
@@ -9256,6 +9287,66 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
break;
};
break;
+ /* pdrfork */
+ case 600:
+ switch (ndx) {
+ case 0:
+ p = "userland int *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* freebsd32_pdwait */
+ case 601:
+ switch (ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "userland int *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "userland struct __wrusage32 *";
+ break;
+ case 4:
+ p = "userland struct __siginfo32 *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* renameat2 */
+ case 602:
+ switch (ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "userland const char *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "userland const char *";
+ break;
+ case 4:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
default:
break;
};
@@ -11174,6 +11265,21 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
if (ndx == 0 || ndx == 1)
p = "int";
break;
+ /* pdrfork */
+ case 600:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* freebsd32_pdwait */
+ case 601:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* renameat2 */
+ case 602:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
default:
break;
};
diff --git a/sys/compat/lindebugfs/lindebugfs.c b/sys/compat/lindebugfs/lindebugfs.c
index 8cddc6f390bc..88b92afd374a 100644
--- a/sys/compat/lindebugfs/lindebugfs.c
+++ b/sys/compat/lindebugfs/lindebugfs.c
@@ -117,9 +117,14 @@ debugfs_fill(PFS_FILL_ARGS)
struct dentry_meta *d;
struct linux_file lf = {};
struct vnode vn;
- char *buf;
- int rc;
- off_t off = 0;
+ struct iovec *iov;
+ size_t cnt, orig_resid;
+ ssize_t rc;
+ off_t off;
+
+ /* Linux file operations assume a pointer to a user buffer. */
+ if (uio->uio_segflg != UIO_USERSPACE)
+ return (EOPNOTSUPP);
if ((rc = linux_set_current_flags(curthread, M_NOWAIT)))
return (rc);
@@ -130,42 +135,64 @@ debugfs_fill(PFS_FILL_ARGS)
rc = d->dm_fops->open(&vn, &lf);
if (rc < 0) {
#ifdef INVARIANTS
- printf("%s:%d open failed with %d\n", __func__, __LINE__, rc);
+ printf("%s:%d open failed with %zd\n", __func__, __LINE__, rc);
#endif
return (-rc);
}
- rc = -ENODEV;
- switch (uio->uio_rw) {
- case UIO_READ:
- if (d->dm_fops->read != NULL) {
- rc = -ENOMEM;
- buf = malloc(sb->s_size, M_DFSINT, M_ZERO | M_NOWAIT);
- if (buf != NULL) {
- rc = d->dm_fops->read(&lf, buf, sb->s_size,
- &off);
- if (rc > 0)
- sbuf_bcpy(sb, buf, strlen(buf));
-
- free(buf, M_DFSINT);
- }
+ off = uio->uio_offset;
+ orig_resid = uio->uio_resid;
+ while (uio->uio_resid > 0) {
+ KASSERT(uio->uio_iovcnt > 0,
+ ("%s: uio %p iovcnt underflow", __func__, uio));
+
+ iov = uio->uio_iov;
+ cnt = iov->iov_len;
+ if (cnt == 0) {
+ uio->uio_iov++;
+ uio->uio_iovcnt--;
+ continue;
}
- break;
- case UIO_WRITE:
- if (d->dm_fops->write != NULL) {
- sbuf_finish(sb);
- rc = d->dm_fops->write(&lf, sbuf_data(sb), sbuf_len(sb),
- &off);
+ if (cnt > uio->uio_resid)
+ cnt = uio->uio_resid;
+
+ switch (uio->uio_rw) {
+ case UIO_READ:
+ if (d->dm_fops->read != NULL)
+ rc = d->dm_fops->read(&lf, iov->iov_base, cnt,
+ &off);
+ else
+ rc = -ENODEV;
+ break;
+ case UIO_WRITE:
+ if (d->dm_fops->write != NULL)
+ rc = d->dm_fops->write(&lf, iov->iov_base, cnt,
+ &off);
+ else
+ rc = -ENODEV;
+ break;
}
- break;
+
+ if (rc <= 0)
+ break;
+
+ iov->iov_base = (char *)iov->iov_base + rc;
+ iov->iov_len -= rc;
+ uio->uio_resid -= rc;
+ uio->uio_offset = off;
}
if (d->dm_fops->release)
d->dm_fops->release(&vn, &lf);
+ /* Return success for short operations. */
+ if (orig_resid != uio->uio_resid)
+ rc = 0;
+
if (rc < 0) {
#ifdef INVARIANTS
- printf("%s:%d read/write failed with %d\n", __func__, __LINE__, rc);
+ printf("%s:%d read/write failed with %zd\n", __func__, __LINE__,
+ rc);
#endif
return (-rc);
}
@@ -207,7 +234,7 @@ debugfs_create_file(const char *name, umode_t mode,
flags = fops->write ? PFS_RDWR : PFS_RD;
pfs_create_file(pnode, &dnode->d_pfs_node, name, debugfs_fill,
- debugfs_attr, NULL, debugfs_destroy, flags | PFS_NOWAIT);
+ debugfs_attr, NULL, debugfs_destroy, flags | PFS_RAW | PFS_NOWAIT);
if (dnode->d_pfs_node == NULL) {
free(dm, M_DFSINT);
return (NULL);
@@ -272,6 +299,9 @@ debugfs_create_dir(const char *name, struct dentry *parent)
struct dentry *dnode;
struct pfs_node *pnode;
+ if (name == NULL)
+ return (NULL);
+
dm = malloc(sizeof(*dm), M_DFSINT, M_NOWAIT | M_ZERO);
if (dm == NULL)
return (NULL);
@@ -668,7 +698,7 @@ fops_str_read(struct file *filp, char __user *ubuf, size_t read_size,
}
static ssize_t
-fops_str_write(struct file *filp, const char *buf, size_t write_size,
+fops_str_write(struct file *filp, const char __user *buf, size_t write_size,
loff_t *ppos)
{
char *old, *new;
diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c
index daddafa325ad..30e79a53ad2a 100644
--- a/sys/compat/linux/linux_file.c
+++ b/sys/compat/linux/linux_file.c
@@ -398,12 +398,50 @@ struct l_dirent64 {
roundup(offsetof(struct l_dirent64, d_name) + (namlen) + 1, \
sizeof(uint64_t))
+/*
+ * Do kern_getdirentries() and then skip over any invalid entries.
+ * (Repeat, if there are no valid entries.)
+ * Adjust bufp and lenp.
+ */
+static int
+linux_getdirentries(struct thread *td, int fd, caddr_t *bufp, int buflen,
+ off_t *basep, int *lenp)
+{
+ struct dirent *bdp;
+ caddr_t buf;
+ int error, len;
+
+ /* Loop around until a valid entry is found or at EOF. */
+ for (;;) {
+ error = kern_getdirentries(td, fd, *bufp, buflen,
+ basep, NULL, UIO_SYSSPACE);
+ if (error != 0)
+ return (error);
+ len = td->td_retval[0];
+ if (len == 0) {
+ *lenp = 0;
+ return (0);
+ }
+ buf = *bufp;
+ while (len > 0) {
+ bdp = (struct dirent *)buf;
+ if (bdp->d_fileno != 0) {
+ *bufp = buf;
+ *lenp = len;
+ return (0);
+ }
+ buf += bdp->d_reclen;
+ len -= bdp->d_reclen;
+ }
+ }
+}
+
#ifdef LINUX_LEGACY_SYSCALLS
int
linux_getdents(struct thread *td, struct linux_getdents_args *args)
{
struct dirent *bdp;
- caddr_t inp, buf; /* BSD-format */
+ caddr_t inp, buf, bufsav; /* BSD-format */
int len, reclen; /* BSD-format */
caddr_t outp; /* Linux-format */
int resid, linuxreclen; /* Linux-format */
@@ -413,11 +451,11 @@ linux_getdents(struct thread *td, struct linux_getdents_args *args)
int buflen, error;
size_t retval;
- buflen = min(args->count, MAXBSIZE);
- buf = malloc(buflen, M_LINUX, M_WAITOK);
+ buflen = min(roundup2(args->count, DEV_BSIZE), MAXBSIZE);
+ bufsav = buf = malloc(buflen, M_LINUX, M_WAITOK);
- error = kern_getdirentries(td, args->fd, buf, buflen,
- &base, NULL, UIO_SYSSPACE);
+ error = linux_getdirentries(td, args->fd, &buf, buflen,
+ &base, &len);
if (error != 0) {
error = linux_getdents_error(td, args->fd, error);
goto out1;
@@ -425,7 +463,6 @@ linux_getdents(struct thread *td, struct linux_getdents_args *args)
lbuf = malloc(LINUX_RECLEN(LINUX_NAME_MAX), M_LINUX, M_WAITOK | M_ZERO);
- len = td->td_retval[0];
inp = buf;
outp = (caddr_t)args->dent;
resid = args->count;
@@ -434,44 +471,47 @@ linux_getdents(struct thread *td, struct linux_getdents_args *args)
while (len > 0) {
bdp = (struct dirent *) inp;
reclen = bdp->d_reclen;
- linuxreclen = LINUX_RECLEN(bdp->d_namlen);
- /*
- * No more space in the user supplied dirent buffer.
- * Return EINVAL.
- */
- if (resid < linuxreclen) {
- error = EINVAL;
- goto out;
+ /* Copy a valid entry out. */
+ if (bdp->d_fileno != 0) {
+ linuxreclen = LINUX_RECLEN(bdp->d_namlen);
+ /*
+ * No more space in the user supplied dirent buffer.
+ * Return EINVAL.
+ */
+ if (resid < linuxreclen) {
+ error = EINVAL;
+ goto out;
+ }
+
+ linux_dirent = (struct l_dirent*)lbuf;
+ linux_dirent->d_ino = bdp->d_fileno;
+ linux_dirent->d_off = bdp->d_off;
+ linux_dirent->d_reclen = linuxreclen;
+ /*
+ * Copy d_type to last byte of l_dirent buffer
+ */
+ lbuf[linuxreclen - 1] = bdp->d_type;
+ strlcpy(linux_dirent->d_name, bdp->d_name,
+ linuxreclen - offsetof(struct l_dirent, d_name)-1);
+ error = copyout(linux_dirent, outp, linuxreclen);
+ if (error != 0)
+ goto out;
+ retval += linuxreclen;
+ outp += linuxreclen;
+ resid -= linuxreclen;
}
- linux_dirent = (struct l_dirent*)lbuf;
- linux_dirent->d_ino = bdp->d_fileno;
- linux_dirent->d_off = bdp->d_off;
- linux_dirent->d_reclen = linuxreclen;
- /*
- * Copy d_type to last byte of l_dirent buffer
- */
- lbuf[linuxreclen - 1] = bdp->d_type;
- strlcpy(linux_dirent->d_name, bdp->d_name,
- linuxreclen - offsetof(struct l_dirent, d_name)-1);
- error = copyout(linux_dirent, outp, linuxreclen);
- if (error != 0)
- goto out;
-
inp += reclen;
base += reclen;
len -= reclen;
- retval += linuxreclen;
- outp += linuxreclen;
- resid -= linuxreclen;
}
td->td_retval[0] = retval;
out:
free(lbuf, M_LINUX);
out1:
- free(buf, M_LINUX);
+ free(bufsav, M_LINUX);
return (error);
}
#endif
@@ -480,7 +520,7 @@ int
linux_getdents64(struct thread *td, struct linux_getdents64_args *args)
{
struct dirent *bdp;
- caddr_t inp, buf; /* BSD-format */
+ caddr_t inp, buf, bufsav; /* BSD-format */
int len, reclen; /* BSD-format */
caddr_t outp; /* Linux-format */
int resid, linuxreclen; /* Linux-format */
@@ -489,11 +529,11 @@ linux_getdents64(struct thread *td, struct linux_getdents64_args *args)
int buflen, error;
size_t retval;
- buflen = min(args->count, MAXBSIZE);
- buf = malloc(buflen, M_LINUX, M_WAITOK);
+ buflen = min(roundup2(args->count, DEV_BSIZE), MAXBSIZE);
+ bufsav = buf = malloc(buflen, M_LINUX, M_WAITOK);
- error = kern_getdirentries(td, args->fd, buf, buflen,
- &base, NULL, UIO_SYSSPACE);
+ error = linux_getdirentries(td, args->fd, &buf, buflen,
+ &base, &len);
if (error != 0) {
error = linux_getdents_error(td, args->fd, error);
goto out1;
@@ -502,7 +542,6 @@ linux_getdents64(struct thread *td, struct linux_getdents64_args *args)
linux_dirent64 = malloc(LINUX_RECLEN64(LINUX_NAME_MAX), M_LINUX,
M_WAITOK | M_ZERO);
- len = td->td_retval[0];
inp = buf;
outp = (caddr_t)args->dirent;
resid = args->count;
@@ -511,40 +550,43 @@ linux_getdents64(struct thread *td, struct linux_getdents64_args *args)
while (len > 0) {
bdp = (struct dirent *) inp;
reclen = bdp->d_reclen;
- linuxreclen = LINUX_RECLEN64(bdp->d_namlen);
- /*
- * No more space in the user supplied dirent buffer.
- * Return EINVAL.
- */
- if (resid < linuxreclen) {
- error = EINVAL;
- goto out;
+ /* Copy a valid entry out. */
+ if (bdp->d_fileno != 0) {
+ linuxreclen = LINUX_RECLEN64(bdp->d_namlen);
+ /*
+ * No more space in the user supplied dirent buffer.
+ * Return EINVAL.
+ */
+ if (resid < linuxreclen) {
+ error = EINVAL;
+ goto out;
+ }
+
+ linux_dirent64->d_ino = bdp->d_fileno;
+ linux_dirent64->d_off = bdp->d_off;
+ linux_dirent64->d_reclen = linuxreclen;
+ linux_dirent64->d_type = bdp->d_type;
+ strlcpy(linux_dirent64->d_name, bdp->d_name,
+ linuxreclen - offsetof(struct l_dirent64, d_name));
+ error = copyout(linux_dirent64, outp, linuxreclen);
+ if (error != 0)
+ goto out;
+ retval += linuxreclen;
+ outp += linuxreclen;
+ resid -= linuxreclen;
}
- linux_dirent64->d_ino = bdp->d_fileno;
- linux_dirent64->d_off = bdp->d_off;
- linux_dirent64->d_reclen = linuxreclen;
- linux_dirent64->d_type = bdp->d_type;
- strlcpy(linux_dirent64->d_name, bdp->d_name,
- linuxreclen - offsetof(struct l_dirent64, d_name));
- error = copyout(linux_dirent64, outp, linuxreclen);
- if (error != 0)
- goto out;
-
inp += reclen;
base += reclen;
len -= reclen;
- retval += linuxreclen;
- outp += linuxreclen;
- resid -= linuxreclen;
}
td->td_retval[0] = retval;
out:
free(linux_dirent64, M_LINUX);
out1:
- free(buf, M_LINUX);
+ free(bufsav, M_LINUX);
return (error);
}
@@ -553,22 +595,22 @@ int
linux_readdir(struct thread *td, struct linux_readdir_args *args)
{
struct dirent *bdp;
- caddr_t buf; /* BSD-format */
+ caddr_t buf, bufsav; /* BSD-format */
int linuxreclen; /* Linux-format */
off_t base;
struct l_dirent *linux_dirent; /* Linux-format */
- int buflen, error;
+ int buflen, error, len;
- buflen = sizeof(*bdp);
- buf = malloc(buflen, M_LINUX, M_WAITOK);
+ buflen = DEV_BSIZE;
+ bufsav = buf = malloc(buflen, M_LINUX, M_WAITOK);
- error = kern_getdirentries(td, args->fd, buf, buflen,
- &base, NULL, UIO_SYSSPACE);
+ error = linux_getdirentries(td, args->fd, &buf, buflen,
+ &base, &len);
if (error != 0) {
error = linux_getdents_error(td, args->fd, error);
goto out;
}
- if (td->td_retval[0] == 0)
+ if (len == 0)
goto out;
linux_dirent = malloc(LINUX_RECLEN(LINUX_NAME_MAX), M_LINUX,
@@ -588,7 +630,7 @@ linux_readdir(struct thread *td, struct linux_readdir_args *args)
free(linux_dirent, M_LINUX);
out:
- free(buf, M_LINUX);
+ free(bufsav, M_LINUX);
return (error);
}
#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
@@ -769,7 +811,7 @@ linux_rename(struct thread *td, struct linux_rename_args *args)
{
return (kern_renameat(td, AT_FDCWD, args->from, AT_FDCWD,
- args->to, UIO_USERSPACE));
+ args->to, UIO_USERSPACE, 0));
}
#endif
@@ -791,23 +833,34 @@ int
linux_renameat2(struct thread *td, struct linux_renameat2_args *args)
{
int olddfd, newdfd;
+ u_int atflags;
- if (args->flags != 0) {
- if (args->flags & ~(LINUX_RENAME_EXCHANGE |
- LINUX_RENAME_NOREPLACE | LINUX_RENAME_WHITEOUT))
- return (EINVAL);
- if (args->flags & LINUX_RENAME_EXCHANGE &&
- args->flags & (LINUX_RENAME_NOREPLACE |
- LINUX_RENAME_WHITEOUT))
+ atflags = 0;
+ if ((args->flags & ~(LINUX_RENAME_EXCHANGE |
+ LINUX_RENAME_NOREPLACE | LINUX_RENAME_WHITEOUT)) != 0)
+ return (EINVAL);
+ if ((args->flags & LINUX_RENAME_EXCHANGE) != 0 &&
+ (args->flags & (LINUX_RENAME_NOREPLACE |
+ LINUX_RENAME_WHITEOUT)) != 0)
+ return (EINVAL);
+ if ((args->flags & LINUX_RENAME_NOREPLACE) != 0) {
+ if ((args->flags & (LINUX_RENAME_EXCHANGE |
+ LINUX_RENAME_WHITEOUT)) != 0)
return (EINVAL);
-#if 0
+ args->flags &= ~LINUX_RENAME_NOREPLACE;
+ atflags |= AT_RENAME_NOREPLACE;
+ }
+
+ if (args->flags != 0) {
/*
* This spams the console on Ubuntu Focal.
*
- * What's needed here is a general mechanism to let users know
- * about missing features without hogging the system.
+ * What's needed here is a general mechanism to let
+ * users know about missing features without hogging
+ * the system.
*/
- linux_msg(td, "renameat2 unsupported flags 0x%x",
+#if 0
+ linux_msg(td, "renameat2 unsupported flags %#x",
args->flags);
#endif
return (EINVAL);
@@ -816,7 +869,7 @@ linux_renameat2(struct thread *td, struct linux_renameat2_args *args)
olddfd = (args->olddfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->olddfd;
newdfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
return (kern_renameat(td, olddfd, args->oldname, newdfd,
- args->newname, UIO_USERSPACE));
+ args->newname, UIO_USERSPACE, atflags));
}
#ifdef LINUX_LEGACY_SYSCALLS
@@ -1171,7 +1224,7 @@ linux_oldumount(struct thread *td, struct linux_oldumount_args *args)
int
linux_umount(struct thread *td, struct linux_umount_args *args)
{
- int flags;
+ uint64_t flags;
flags = 0;
if ((args->flags & LINUX_MNT_FORCE) != 0) {
diff --git a/sys/compat/linux/linux_if.c b/sys/compat/linux/linux_if.c
index f201f7c4b128..ca4e3a4079ed 100644
--- a/sys/compat/linux/linux_if.c
+++ b/sys/compat/linux/linux_if.c
@@ -105,12 +105,13 @@ static void
linux_ifnet_vnet_uninit(void *arg __unused)
{
/*
- * At a normal vnet shutdown all interfaces are gone at this point.
- * But when we kldunload linux.ko, the vnet_deregister_sysuninit()
- * would call this function for the default vnet.
+ * All cloned interfaces are already gone at this point, as well
+ * as interfaces that were if_vmove'd into this vnet. However,
+ * if a jail has created IFT_ETHER interfaces in self, or has had
+ * physical Ethernet drivers attached in self, than we may have
+ * allocated entries in the unr(9), so clear it to avoid KASSERT.
*/
- if (IS_DEFAULT_VNET(curvnet))
- clear_unrhdr(V_linux_eth_unr);
+ clear_unrhdr(V_linux_eth_unr);
delete_unrhdr(V_linux_eth_unr);
}
VNET_SYSUNINIT(linux_ifnet_vnet_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY,
diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c
index ceb17bd040b5..d2fa0331026b 100644
--- a/sys/compat/linux/linux_ioctl.c
+++ b/sys/compat/linux/linux_ioctl.c
@@ -58,6 +58,7 @@
#include <net/if_types.h>
#include <dev/evdev/input.h>
+#include <dev/hid/hidraw.h>
#include <dev/usb/usb_ioctl.h>
#ifdef COMPAT_LINUX32
@@ -113,6 +114,7 @@ DEFINE_LINUX_IOCTL_SET(kcov, KCOV);
#ifndef COMPAT_LINUX32
DEFINE_LINUX_IOCTL_SET(nvme, NVME);
#endif
+DEFINE_LINUX_IOCTL_SET(hidraw, HIDRAW);
#undef DEFINE_LINUX_IOCTL_SET
@@ -331,6 +333,17 @@ struct linux_termios {
unsigned char c_cc[LINUX_NCCS];
};
+struct linux_termios2 {
+ unsigned int c_iflag;
+ unsigned int c_oflag;
+ unsigned int c_cflag;
+ unsigned int c_lflag;
+ unsigned char c_line;
+ unsigned char c_cc[LINUX_NCCS];
+ unsigned int c_ispeed;
+ unsigned int c_ospeed;
+};
+
struct linux_winsize {
unsigned short ws_row, ws_col;
unsigned short ws_xpixel, ws_ypixel;
@@ -386,7 +399,7 @@ bsd_to_linux_speed(int speed, struct speedtab *table)
for ( ; table->sp_speed != -1; table++)
if (table->sp_speed == speed)
return (table->sp_code);
- return (-1);
+ return (LINUX_BOTHER);
}
static void
@@ -509,6 +522,14 @@ bsd_to_linux_termios(struct termios *bios, struct linux_termios *lios)
}
static void
+bsd_to_linux_termios2(struct termios *bios, struct linux_termios2 *lios2)
+{
+ bsd_to_linux_termios(bios, (struct linux_termios *)lios2);
+ lios2->c_ospeed = bios->c_ospeed;
+ lios2->c_ispeed = bios->c_ispeed;
+}
+
+static void
linux_to_bsd_termios(struct linux_termios *lios, struct termios *bios)
{
int i;
@@ -629,6 +650,16 @@ linux_to_bsd_termios(struct linux_termios *lios, struct termios *bios)
}
static void
+linux_to_bsd_termios2(struct linux_termios2 *lios2, struct termios *bios)
+{
+ linux_to_bsd_termios((struct linux_termios *)lios2, bios);
+ if ((lios2->c_cflag & LINUX_CBAUD) == LINUX_BOTHER)
+ bios->c_ospeed = lios2->c_ospeed;
+ if ((lios2->c_cflag & LINUX_CIBAUD) == LINUX_BOTHER << LINUX_IBSHIFT)
+ bios->c_ispeed = lios2->c_ispeed;
+}
+
+static void
bsd_to_linux_termio(struct termios *bios, struct linux_termio *lio)
{
struct linux_termios lios;
@@ -664,6 +695,7 @@ linux_ioctl_termio(struct thread *td, struct linux_ioctl_args *args)
{
struct termios bios;
struct linux_termios lios;
+ struct linux_termios2 lios2;
struct linux_termio lio;
struct file *fp;
int error;
@@ -1001,6 +1033,43 @@ linux_ioctl_termio(struct thread *td, struct linux_ioctl_args *args)
args->cmd = TIOCCBRK;
error = (sys_ioctl(td, (struct ioctl_args *)args));
break;
+
+ case LINUX_TCGETS2:
+ error = fo_ioctl(fp, TIOCGETA, (caddr_t)&bios, td->td_ucred,
+ td);
+ if (error)
+ break;
+ bsd_to_linux_termios2(&bios, &lios2);
+ error = copyout(&lios2, (void *)args->arg, sizeof(lios2));
+ break;
+
+ case LINUX_TCSETS2:
+ error = copyin((void *)args->arg, &lios2, sizeof(lios2));
+ if (error)
+ break;
+ linux_to_bsd_termios2(&lios2, &bios);
+ error = (fo_ioctl(fp, TIOCSETA, (caddr_t)&bios, td->td_ucred,
+ td));
+ break;
+
+ case LINUX_TCSETSW2:
+ error = copyin((void *)args->arg, &lios2, sizeof(lios2));
+ if (error)
+ break;
+ linux_to_bsd_termios2(&lios2, &bios);
+ error = (fo_ioctl(fp, TIOCSETAW, (caddr_t)&bios, td->td_ucred,
+ td));
+ break;
+
+ case LINUX_TCSETSF2:
+ error = copyin((void *)args->arg, &lios2, sizeof(lios2));
+ if (error)
+ break;
+ linux_to_bsd_termios2(&lios2, &bios);
+ error = (fo_ioctl(fp, TIOCSETAF, (caddr_t)&bios, td->td_ucred,
+ td));
+ break;
+
case LINUX_TIOCGPTN: {
int nb;
@@ -3570,6 +3639,55 @@ linux_ioctl_nvme(struct thread *td, struct linux_ioctl_args *args)
}
#endif
+static int
+linux_ioctl_hidraw(struct thread *td, struct linux_ioctl_args *args)
+{
+ int len = (args->cmd & 0x3fff0000) >> 16;
+ if (len > 8192)
+ return (EINVAL);
+
+ switch (args->cmd & 0xffff) {
+ case LINUX_HIDIOCGRDESCSIZE:
+ args->cmd = HIDIOCGRDESCSIZE;
+ break;
+ case LINUX_HIDIOCGRDESC:
+ args->cmd = HIDIOCGRDESC;
+ break;
+ case LINUX_HIDIOCGRAWINFO:
+ args->cmd = HIDIOCGRAWINFO;
+ break;
+ case LINUX_HIDIOCGRAWNAME:
+ args->cmd = HIDIOCGRAWNAME(len);
+ break;
+ case LINUX_HIDIOCGRAWPHYS:
+ args->cmd = HIDIOCGRAWPHYS(len);
+ break;
+ case LINUX_HIDIOCSFEATURE:
+ args->cmd = HIDIOCSFEATURE(len);
+ break;
+ case LINUX_HIDIOCGFEATURE:
+ args->cmd = HIDIOCGFEATURE(len);
+ break;
+ case LINUX_HIDIOCGRAWUNIQ:
+ args->cmd = HIDIOCGRAWUNIQ(len);
+ break;
+ case LINUX_HIDIOCSINPUT:
+ args->cmd = HIDIOCSINPUT(len);
+ break;
+ case LINUX_HIDIOCGINPUT:
+ args->cmd = HIDIOCGINPUT(len);
+ break;
+ case LINUX_HIDIOCSOUTPUT:
+ args->cmd = HIDIOCSOUTPUT(len);
+ break;
+ case LINUX_HIDIOCGOUTPUT:
+ args->cmd = HIDIOCGOUTPUT(len);
+ break;
+ }
+
+ return (sys_ioctl(td, (struct ioctl_args *)args));
+}
+
/*
* main ioctl syscall function
*/
diff --git a/sys/compat/linux/linux_ioctl.h b/sys/compat/linux/linux_ioctl.h
index 8345b7e4b719..116a4e676228 100644
--- a/sys/compat/linux/linux_ioctl.h
+++ b/sys/compat/linux/linux_ioctl.h
@@ -383,6 +383,11 @@
#define LINUX_TIOCSBRK 0x5427
#define LINUX_TIOCCBRK 0x5428
+#define LINUX_TCGETS2 0x542A
+#define LINUX_TCSETS2 0x542B
+#define LINUX_TCSETSW2 0x542C
+#define LINUX_TCSETSF2 0x542D
+
#define LINUX_TIOCGPTN 0x5430
#define LINUX_TIOCSPTLCK 0x5431
@@ -501,6 +506,7 @@
#define LINUX_FF1 0x0008000
#define LINUX_CBAUD 0x0000100f
+#define LINUX_CIBAUD (LINUX_CBAUD << LINUX_IBSHIFT)
#define LINUX_B0 0x00000000
#define LINUX_B50 0x00000001
@@ -537,8 +543,12 @@
#define LINUX_HUPCL 0x00000400
#define LINUX_CLOCAL 0x00000800
+#define LINUX_BOTHER 0x00001000
+
#define LINUX_CRTSCTS 0x80000000
+#define LINUX_IBSHIFT 16
+
/* Linux c_lflag masks */
#define LINUX_ISIG 0x00000001
#define LINUX_ICANON 0x00000002
@@ -797,6 +807,25 @@
#define LINUX_IOCTL_NVME_MAX LINUX_NVME_IOCTL_RESCAN
/*
+ * hidraw
+ */
+#define LINUX_HIDIOCGRDESCSIZE 0x4801
+#define LINUX_HIDIOCGRDESC 0x4802
+#define LINUX_HIDIOCGRAWINFO 0x4803
+#define LINUX_HIDIOCGRAWNAME 0x4804
+#define LINUX_HIDIOCGRAWPHYS 0x4805
+#define LINUX_HIDIOCSFEATURE 0x4806
+#define LINUX_HIDIOCGFEATURE 0x4807
+#define LINUX_HIDIOCGRAWUNIQ 0x4808
+#define LINUX_HIDIOCSINPUT 0x4809
+#define LINUX_HIDIOCGINPUT 0x480A
+#define LINUX_HIDIOCSOUTPUT 0x480B
+#define LINUX_HIDIOCGOUTPUT 0x480C
+
+#define LINUX_IOCTL_HIDRAW_MIN LINUX_HIDIOCGRDESCSIZE
+#define LINUX_IOCTL_HIDRAW_MAX LINUX_HIDIOCGOUTPUT
+
+/*
* Pluggable ioctl handlers
*/
struct linux_ioctl_args;
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
index 69fb9935a9ae..937b010c8435 100644
--- a/sys/compat/linux/linux_misc.c
+++ b/sys/compat/linux/linux_misc.c
@@ -750,6 +750,7 @@ linux_common_wait(struct thread *td, idtype_t idtype, int id, int *statusp,
error = linux_copyout_rusage(&wru.wru_self, rup);
if (error == 0 && infop != NULL && td->td_retval[0] != 0) {
sig = bsd_to_linux_signal(siginfo.si_signo);
+ memset(&lsi, 0, sizeof(lsi));
siginfo_to_lsiginfo(&siginfo, &lsi, sig);
error = copyout(&lsi, infop, sizeof(lsi));
}
diff --git a/sys/compat/linux/linux_netlink.c b/sys/compat/linux/linux_netlink.c
index 6dd2ad7ad8b0..256f188385ce 100644
--- a/sys/compat/linux/linux_netlink.c
+++ b/sys/compat/linux/linux_netlink.c
@@ -340,7 +340,6 @@ rtnl_if_flags_to_linux(unsigned int if_flags)
case IFF_STATICARP:
case IFF_STICKYARP:
case IFF_DYING:
- case IFF_RENAMING:
/* No Linux analogue */
break;
case IFF_MULTICAST:
diff --git a/sys/compat/linux/linux_timer.c b/sys/compat/linux/linux_timer.c
index ed9133359302..230be9572b85 100644
--- a/sys/compat/linux/linux_timer.c
+++ b/sys/compat/linux/linux_timer.c
@@ -131,7 +131,7 @@ linux_timer_settime(struct thread *td, struct linux_timer_settime_args *uap)
return (error);
error = kern_ktimer_settime(td, uap->timerid, flags, &val, ovalp);
if (error == 0 && uap->old != NULL) {
- error = native_to_linux_itimerspec(&l_val, &val);
+ error = native_to_linux_itimerspec(&l_oval, &oval);
if (error == 0)
error = copyout(&l_oval, uap->old, sizeof(l_oval));
}
@@ -158,7 +158,7 @@ linux_timer_settime64(struct thread *td, struct linux_timer_settime64_args *uap)
return (error);
error = kern_ktimer_settime(td, uap->timerid, flags, &val, ovalp);
if (error == 0 && uap->old != NULL) {
- error = native_to_linux_itimerspec64(&l_val, &val);
+ error = native_to_linux_itimerspec64(&l_oval, &oval);
if (error == 0)
error = copyout(&l_oval, uap->old, sizeof(l_oval));
}
diff --git a/sys/compat/linuxkpi/common/include/asm/unaligned.h b/sys/compat/linuxkpi/common/include/asm/unaligned.h
index e45846a3b543..6778e9fcede1 100644
--- a/sys/compat/linuxkpi/common/include/asm/unaligned.h
+++ b/sys/compat/linuxkpi/common/include/asm/unaligned.h
@@ -48,6 +48,13 @@ get_unaligned_le32(const void *p)
return (le32_to_cpup((const __le32 *)p));
}
+static __inline uint64_t
+get_unaligned_le64(const void *p)
+{
+
+ return (le64_to_cpup((const __le64 *)p));
+}
+
static __inline void
put_unaligned_le16(__le16 v, void *p)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/acpi.h b/sys/compat/linuxkpi/common/include/linux/acpi.h
index 3e1ec1b20626..a764a975c983 100644
--- a/sys/compat/linuxkpi/common/include/linux/acpi.h
+++ b/sys/compat/linuxkpi/common/include/linux/acpi.h
@@ -32,7 +32,7 @@
#include <linux/device.h>
#include <linux/uuid.h>
-#if defined(__aarch64__) || defined(__amd64__) || defined(__i386__)
+#if defined(__aarch64__) || defined(__amd64__) || defined(__i386__) || defined(__riscv)
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
diff --git a/sys/compat/linuxkpi/common/include/linux/bcm47xx_nvram.h b/sys/compat/linuxkpi/common/include/linux/bcm47xx_nvram.h
new file mode 100644
index 000000000000..744101a2f8b1
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/bcm47xx_nvram.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2025 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_BCM47XX_NVRAM_H
+#define _LINUXKPI_LINUX_BCM47XX_NVRAM_H
+
+static inline char *
+bcm47xx_nvram_get_contents(size_t *x __unused)
+{
+ return (NULL);
+};
+
+static inline void
+bcm47xx_nvram_release_contents(const char *x __unused)
+{
+};
+
+#endif /* _LINUXKPI_LINUX_BCM47XX_NVRAM_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/bcma/bcma.h b/sys/compat/linuxkpi/common/include/linux/bcma/bcma.h
new file mode 100644
index 000000000000..3840c3a420e5
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/bcma/bcma.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2025 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_BCMA_BCMA_H
+#define _LINUXKPI_LINUX_BCMA_BCMA_H
+
+#define BCMA_CORE_80211 0x812
+#define BCMA_CORE_ARM_CA7 0x847
+#define BCMA_CORE_ARM_CM3 0x82A
+#define BCMA_CORE_ARM_CR4 0x83E
+#define BCMA_CORE_CHIPCOMMON 0x800
+#define BCMA_CORE_GCI 0x840
+#define BCMA_CORE_INTERNAL_MEM 0x80E
+#define BCMA_CORE_PCIE2 0x83C
+#define BCMA_CORE_PMU 0x827
+#define BCMA_CORE_SDIO_DEV 0x829
+#define BCMA_CORE_SYS_MEM 0x849
+
+/* XXX not sure where these belong. */
+#define BCMA_CC_CAP_EXT_AOB_PRESENT 0x00000040
+#define BCMA_CC_PMU_CTL_RES_SHIFT 13
+#define BCMA_CC_PMU_CTL_RES_RELOAD 0x2
+#define BCMA_CC_SROM_CONTROL_OTPSEL 0x00000010
+#define BCMA_CC_SROM_CONTROL_OTP_PRESENT 0x00000020
+
+#endif /* _LINUXKPI_LINUX_BCMA_BCMA_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/bcma/bcma_regs.h b/sys/compat/linuxkpi/common/include/linux/bcma/bcma_regs.h
new file mode 100644
index 000000000000..0a4cdddf7a73
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/bcma/bcma_regs.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2025 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_BCMA_BCMA_REGS_H
+#define _LINUXKPI_LINUX_BCMA_BCMA_REGS_H
+
+#define BCMA_IOCTL 0x0408
+#define BCMA_IOCTL_CLK 0x0001
+#define BCMA_IOCTL_FGC 0x0002
+
+#define BCMA_RESET_CTL 0x0800
+#define BCMA_RESET_CTL_RESET 0x0001
+
+#endif /* _LINUXKPI_LINUX_BCMA_BCMA_REGS_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/bitops.h b/sys/compat/linuxkpi/common/include/linux/bitops.h
index 8fac80820f30..125081ab5b74 100644
--- a/sys/compat/linuxkpi/common/include/linux/bitops.h
+++ b/sys/compat/linuxkpi/common/include/linux/bitops.h
@@ -51,12 +51,6 @@
#define BITS_PER_TYPE(t) (sizeof(t) * BITS_PER_BYTE)
#define BITS_TO_BYTES(n) howmany((n), BITS_PER_BYTE)
-#define hweight8(x) bitcount((uint8_t)(x))
-#define hweight16(x) bitcount16(x)
-#define hweight32(x) bitcount32(x)
-#define hweight64(x) bitcount64(x)
-#define hweight_long(x) bitcountl(x)
-
#if __has_builtin(__builtin_popcountg)
#define HWEIGHT8(x) (__builtin_popcountg((uint8_t)(x)))
#define HWEIGHT16(x) (__builtin_popcountg((uint16_t)(x)))
@@ -70,6 +64,12 @@
#define HWEIGHT64(x) (__const_bitcount64((uint64_t)(x)))
#endif
+#define hweight8(x) (__builtin_constant_p(x) ? HWEIGHT8(x) : bitcount((uint8_t)(x)))
+#define hweight16(x) (__builtin_constant_p(x) ? HWEIGHT16(x) : bitcount16(x))
+#define hweight32(x) (__builtin_constant_p(x) ? HWEIGHT32(x) : bitcount32(x))
+#define hweight64(x) (__builtin_constant_p(x) ? HWEIGHT64(x) : bitcount64(x))
+#define hweight_long(x) bitcountl(x)
+
static inline int
__ffs(int mask)
{
@@ -437,4 +437,16 @@ sign_extend32(uint32_t value, int index)
return ((int32_t)(value << shift) >> shift);
}
+static inline uint64_t
+rol64(uint64_t word, unsigned int shift)
+{
+ return ((word << (shift & 63)) | (word >> ((-shift) & 63)));
+}
+
+static inline uint32_t
+rol32(uint32_t word, unsigned int shift)
+{
+ return ((word << (shift & 31)) | (word >> ((-shift) & 31)));
+}
+
#endif /* _LINUXKPI_LINUX_BITOPS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/cec-funcs.h b/sys/compat/linuxkpi/common/include/linux/cec-funcs.h
new file mode 100644
index 000000000000..1107b04e4e08
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/cec-funcs.h
@@ -0,0 +1,16 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ */
+
+#ifndef _LINUXKPI_LINUX_CEC_FUNCS_H_
+#define _LINUXKPI_LINUX_CEC_FUNCS_H_
+
+#include <linux/cec.h>
+
+#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/cec.h b/sys/compat/linuxkpi/common/include/linux/cec.h
index e0854d87d85c..b08d891537a9 100644
--- a/sys/compat/linuxkpi/common/include/linux/cec.h
+++ b/sys/compat/linuxkpi/common/include/linux/cec.h
@@ -3,6 +3,9 @@
#ifndef _LINUXKPI_LINUX_CEC_H_
#define _LINUXKPI_LINUX_CEC_H_
+#include <linux/types.h>
+#include <linux/string.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
index a9dd22fd0f4c..c75404fd6cf3 100644
--- a/sys/compat/linuxkpi/common/include/linux/cgroup.h
+++ b/sys/compat/linuxkpi/common/include/linux/cgroup.h
@@ -29,6 +29,15 @@
#ifndef _LINUXKPI_LINUX_CGROUP_H_
#define _LINUXKPI_LINUX_CGROUP_H_
+#include <linux/sched.h>
+#include <linux/nodemask.h>
+#include <linux/list.h>
+#include <linux/rculist.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/jump_label.h>
+#include <linux/types.h>
+#include <linux/refcount.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
index 5bb146f082ed..fb21a81f121b 100644
--- a/sys/compat/linuxkpi/common/include/linux/cleanup.h
+++ b/sys/compat/linuxkpi/common/include/linux/cleanup.h
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2024-2025 The FreeBSD Foundation
+ * Copyright (c) 2024-2026 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -10,18 +10,26 @@
#ifndef _LINUXKPI_LINUX_CLEANUP_H
#define _LINUXKPI_LINUX_CLEANUP_H
+#include <linux/err.h>
+
+#define CLEANUP_NAME(_n, _s) __CONCAT(__CONCAT(cleanup_, _n), _s)
+
#define __cleanup(_f) __attribute__((__cleanup__(_f)))
+#define DECLARE(_n, _x) \
+ CLEANUP_NAME(_n, _t) _x __cleanup(CLEANUP_NAME(_n, _destroy)) = \
+ CLEANUP_NAME(_n, _create)
+
/*
* 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; \
+ typedef _dt CLEANUP_NAME(_n, _t); \
\
static inline _dt \
- guard_ ## _n ## _create( _dt _T) \
+ CLEANUP_NAME(_n, _create)( _dt _T) \
{ \
_dt c; \
\
@@ -30,7 +38,7 @@
} \
\
static inline void \
- guard_ ## _n ## _destroy(_dt *t) \
+ CLEANUP_NAME(_n, _destroy)(_dt *t) \
{ \
_dt _T; \
\
@@ -39,9 +47,10 @@
}
/* We need to keep these calls unique. */
+#define _guard(_n, _x) \
+ DECLARE(_n, _x)
#define guard(_n) \
- guard_ ## _n ## _t guard_ ## _n ## _ ## __COUNTER__ \
- __cleanup(guard_ ## _n ## _destroy) = guard_ ## _n ## _create
+ _guard(_n, guard_ ## _n ## _ ## __COUNTER__)
#define DEFINE_FREE(_n, _t, _f) \
static inline void \
@@ -56,38 +65,100 @@
#define __free(_n) __cleanup(__free_##_n)
/*
- * Given this is a _0 version it should likely be broken up into parts.
- * But we have no idead what a _1, _2, ... version would do different
- * until we see a call.
- * This is used for a not-real-type (rcu). We use a bool to "simulate"
- * the lock held. Also _T still special, may not always be used, so tag
- * with __unused (or better the LinuxKPI __maybe_unused).
+ * Our initial version go broken up. Some simplifications like using
+ * "bool" for the lock had to be changed to a more general type.
+ * _T is still special and, like other bits, may not always be used,
+ * so tag with __unused (or better the LinuxKPI __maybe_unused).
*/
-#define DEFINE_LOCK_GUARD_0(_n, _lock, _unlock, ...) \
+#define _DEFINE_LOCK_GUARD_0(_n, _lock) \
+ static inline CLEANUP_NAME(_n, _t) \
+ CLEANUP_NAME(_n, _create)(void) \
+ { \
+ CLEANUP_NAME(_n, _t) _tmp; \
+ CLEANUP_NAME(_n, _t) *_T __maybe_unused; \
+ \
+ _tmp.lock = (void *)1; \
+ _T = &_tmp; \
+ _lock; \
+ return (_tmp); \
+ }
+
+#define _DEFINE_LOCK_GUARD_1(_n, _type, _lock) \
+ static inline CLEANUP_NAME(_n, _t) \
+ CLEANUP_NAME(_n, _create)(_type *l) \
+ { \
+ CLEANUP_NAME(_n, _t) _tmp; \
+ CLEANUP_NAME(_n, _t) *_T __maybe_unused; \
\
+ _tmp.lock = l; \
+ _T = &_tmp; \
+ _lock; \
+ return (_tmp); \
+ }
+
+#define _GUARD_IS_ERR(_v) \
+ ({ \
+ uintptr_t x = (uintptr_t)(void *)(_v); \
+ IS_ERR_VALUE(x); \
+ })
+
+#define __is_cond_ptr(_n) \
+ CLEANUP_NAME(_n, _is_cond)
+#define __guard_ptr(_n) \
+ CLEANUP_NAME(_n, _ptr)
+
+#define _DEFINE_CLEANUP_IS_CONDITIONAL(_n, _b) \
+ static const bool CLEANUP_NAME(_n, _is_cond) __maybe_unused = _b
+
+#define _DEFINE_GUARD_LOCK_PTR(_n, _lp) \
+ static inline void * \
+ CLEANUP_NAME(_n, _lock_ptr)(CLEANUP_NAME(_n, _t) *_T) \
+ { \
+ void *_p; \
+ \
+ _p = (void *)(uintptr_t)*(_lp); \
+ if (IS_ERR(_p)) \
+ _p = NULL; \
+ return (_p); \
+ }
+
+#define _DEFINE_UNLOCK_GUARD(_n, _type, _unlock, ...) \
typedef struct { \
- bool lock; \
+ _type *lock; \
__VA_ARGS__; \
- } guard_ ## _n ## _t; \
+ } CLEANUP_NAME(_n, _t); \
\
static inline void \
- guard_ ## _n ## _destroy(guard_ ## _n ## _t *_T) \
+ CLEANUP_NAME(_n, _destroy)(CLEANUP_NAME(_n, _t) *_T) \
{ \
- if (_T->lock) { \
+ if (!_GUARD_IS_ERR(_T->lock)) { \
_unlock; \
} \
} \
\
- static inline guard_ ## _n ## _t \
- guard_ ## _n ## _create(void) \
- { \
- guard_ ## _n ## _t _tmp; \
- guard_ ## _n ## _t *_T __maybe_unused; \
- \
- _tmp.lock = true; \
- _T = &_tmp; \
- _lock; \
- return (_tmp); \
- }
+ _DEFINE_GUARD_LOCK_PTR(_n, &_T->lock)
+
+#define DEFINE_LOCK_GUARD_0(_n, _lock, _unlock, ...) \
+ _DEFINE_CLEANUP_IS_CONDITIONAL(_n, false); \
+ _DEFINE_UNLOCK_GUARD(_n, void, _unlock, __VA_ARGS__) \
+ _DEFINE_LOCK_GUARD_0(_n, _lock)
+
+/* This allows the type to be set. */
+#define DEFINE_LOCK_GUARD_1(_n, _t, _lock, _unlock, ...) \
+ _DEFINE_CLEANUP_IS_CONDITIONAL(_n, false); \
+ _DEFINE_UNLOCK_GUARD(_n, _t, _unlock, __VA_ARGS__) \
+ _DEFINE_LOCK_GUARD_1(_n, _t, _lock)
+
+#define _scoped_guard(_n, _l, ...) \
+ for (DECLARE(_n, _scoped)(__VA_ARGS__); \
+ 1 /*__guard_ptr(_n)(&_scoped) || !__is_cond_ptr(_n) */; \
+ ({ goto _l; })) \
+ if (0) { \
+_l: \
+ break; \
+ } else
+
+#define scoped_guard(_n, ...) \
+ _scoped_guard(_n, ___label_ ## __COUNTER__, ##__VA_ARGS__)
#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 4146c829b936..90d907dd4d45 100644
--- a/sys/compat/linuxkpi/common/include/linux/compiler.h
+++ b/sys/compat/linuxkpi/common/include/linux/compiler.h
@@ -33,48 +33,18 @@
#include <sys/cdefs.h>
#include <sys/endian.h>
-#define __user
-#define __kernel
-#define __safe
-#define __force
-#define __nocast
-#define __iomem
-#define __chk_user_ptr(x) ((void)0)
-#define __chk_io_ptr(x) ((void)0)
-#define __builtin_warning(x, y...) (1)
-#define __acquires(x)
-#define __releases(x)
-#define __acquire(x) do { } while (0)
-#define __release(x) do { } while (0)
-#define __cond_lock(x,c) (c)
+#include <compat/linuxkpi/common/include/linux/compiler_types.h>
+
#define __bitwise
#define __devinitdata
-#ifndef __deprecated
-#define __deprecated
-#endif
#define __init
#define __initconst
#define __devinit
#define __devexit
#define __exit
-#define __rcu
-#define __percpu
-#define __weak __weak_symbol
-#define __malloc
-#define __attribute_const__ __attribute__((__const__))
-#undef __always_inline
-#define __always_inline inline
-#define noinline __noinline
-#define noinline_for_stack __noinline
#define ____cacheline_aligned __aligned(CACHE_LINE_SIZE)
#define ____cacheline_aligned_in_smp __aligned(CACHE_LINE_SIZE)
-#define fallthrough /* FALLTHROUGH */ do { } while(0)
-#if __has_attribute(__nonstring__)
-#define __nonstring __attribute__((__nonstring__))
-#else
-#define __nonstring
-#endif
#if __has_attribute(__counted_by__)
#define __counted_by(_x) __attribute__((__counted_by__(_x)))
#else
@@ -93,24 +63,12 @@
#define typeof(x) __typeof(x)
#define uninitialized_var(x) x = x
-#define __maybe_unused __unused
-#define __always_unused __unused
-#define __must_check __result_use_check
-
-#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))
#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
-#define ___PASTE(a,b) a##b
-#define __PASTE(a,b) ___PASTE(a,b)
-
#define WRITE_ONCE(x,v) do { \
barrier(); \
(*(volatile __typeof(x) *)(uintptr_t)&(x)) = (v); \
@@ -129,8 +87,6 @@
#define lockless_dereference(p) READ_ONCE(p)
#define _AT(T,X) ((T)(X))
-
-#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define __must_be_array(a) __same_type(a, &(a)[0])
#define sizeof_field(_s, _m) sizeof(((_s *)0)->_m)
diff --git a/sys/compat/linuxkpi/common/include/linux/compiler_attributes.h b/sys/compat/linuxkpi/common/include/linux/compiler_attributes.h
new file mode 100644
index 000000000000..42908bb6c2b5
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/compiler_attributes.h
@@ -0,0 +1,49 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2010 Isilon Systems, Inc.
+ * Copyright (c) 2010 iX Systems, Inc.
+ * Copyright (c) 2010 Panasas, Inc.
+ * Copyright (c) 2013-2016 Mellanox Technologies, Ltd.
+ * Copyright (c) 2015 François Tigeot
+ * All rights reserved.
+ */
+
+#ifndef _LINUXKPI_LINUX_COMPILER_ATTRIBUTES_H_
+#define _LINUXKPI_LINUX_COMPILER_ATTRIBUTES_H_
+
+#include <sys/cdefs.h>
+
+#define __attribute_const__ __attribute__((__const__))
+
+#ifndef __deprecated
+#define __deprecated
+#endif
+
+#define fallthrough /* FALLTHROUGH */ do { } while(0)
+
+#undef __always_inline
+#define __always_inline inline
+
+#define __printf(a,b) __printflike(a,b)
+
+#define __malloc
+
+#define noinline __noinline
+
+#if __has_attribute(__nonstring__)
+#define __nonstring __attribute__((__nonstring__))
+#else
+#define __nonstring
+#endif
+
+#define noinline_for_stack __noinline
+
+#define __maybe_unused __unused
+#define __always_unused __unused
+
+#define __must_check __result_use_check
+
+#define __weak __weak_symbol
+
+#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/compiler_types.h b/sys/compat/linuxkpi/common/include/linux/compiler_types.h
new file mode 100644
index 000000000000..7151c03de690
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/compiler_types.h
@@ -0,0 +1,45 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2010 Isilon Systems, Inc.
+ * Copyright (c) 2010 iX Systems, Inc.
+ * Copyright (c) 2010 Panasas, Inc.
+ * Copyright (c) 2013-2016 Mellanox Technologies, Ltd.
+ * Copyright (c) 2015 François Tigeot
+ * All rights reserved.
+ */
+
+#ifndef _LINUXKPI_LINUX_COMPILER_TYPES_H_
+#define _LINUXKPI_LINUX_COMPILER_TYPES_H_
+
+#include <sys/cdefs.h>
+
+#include <compat/linuxkpi/common/include/linux/compiler_attributes.h>
+
+#define __kernel
+#define __user
+#define __iomem
+#define __percpu
+#define __rcu
+#define __chk_user_ptr(x) ((void)0)
+#define __chk_io_ptr(x) ((void)0)
+#define __acquires(x)
+#define __releases(x)
+#define __acquire(x) do { } while (0)
+#define __release(x) do { } while (0)
+#define __cond_lock(x,c) (c)
+#define __force
+#define __nocast
+#define __safe
+#define __builtin_warning(x, y...) (1)
+
+#define ___PASTE(a,b) a##b
+#define __PASTE(a,b) ___PASTE(a,b)
+
+#define __diag_push()
+#define __diag_pop()
+#define __diag_ignore_all(...)
+
+#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
+
+#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
index 2d8e1196d3d3..5e5d40ef8339 100644
--- a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
+++ b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
@@ -96,6 +96,8 @@ void *linux_dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag);
void *linuxkpi_dmam_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag);
+void linuxkpi_dmam_free_coherent(struct device *dev, size_t size,
+ void *addr, dma_addr_t dma_handle);
dma_addr_t linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len); /* backward compat */
dma_addr_t lkpi_dma_map_phys(struct device *, vm_paddr_t, size_t,
enum dma_data_direction, unsigned long);
@@ -104,10 +106,10 @@ 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 direction,
- unsigned long attrs __unused);
+ unsigned long attrs);
void linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
int nents __unused, enum dma_data_direction direction,
- unsigned long attrs __unused);
+ unsigned long attrs);
void linuxkpi_dma_sync(struct device *, dma_addr_t, size_t, bus_dmasync_op_t);
static inline int
@@ -181,6 +183,13 @@ dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
kmem_free(cpu_addr, size);
}
+static inline void
+dmam_free_coherent(struct device *dev, size_t size, void *addr,
+ dma_addr_t dma_handle)
+{
+ linuxkpi_dmam_free_coherent(dev, size, addr, dma_handle);
+}
+
static inline dma_addr_t
dma_map_page_attrs(struct device *dev, struct page *page, size_t offset,
size_t size, enum dma_data_direction direction, unsigned long attrs)
@@ -192,10 +201,10 @@ dma_map_page_attrs(struct device *dev, struct page *page, size_t offset,
/* linux_dma_(un)map_sg_attrs does not support attrs yet */
#define dma_map_sg_attrs(dev, sgl, nents, dir, attrs) \
- linux_dma_map_sg_attrs(dev, sgl, nents, dir, 0)
+ linux_dma_map_sg_attrs(dev, sgl, nents, dir, attrs)
#define dma_unmap_sg_attrs(dev, sg, nents, dir, attrs) \
- linux_dma_unmap_sg_attrs(dev, sg, nents, dir, 0)
+ linux_dma_unmap_sg_attrs(dev, sg, nents, dir, attrs)
static inline dma_addr_t
dma_map_page(struct device *dev, struct page *page,
@@ -352,10 +361,10 @@ dma_max_mapping_size(struct device *dev)
}
#define dma_map_single_attrs(dev, ptr, size, dir, attrs) \
- _dma_map_single_attrs(dev, ptr, size, dir, 0)
+ _dma_map_single_attrs(dev, ptr, size, dir, attrs)
#define dma_unmap_single_attrs(dev, dma_addr, size, dir, attrs) \
- _dma_unmap_single_attrs(dev, dma_addr, size, dir, 0)
+ _dma_unmap_single_attrs(dev, dma_addr, size, dir, attrs)
#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, 0)
#define dma_unmap_single(d, a, s, r) dma_unmap_single_attrs(d, a, s, r, 0)
diff --git a/sys/compat/linuxkpi/common/include/linux/eventfd.h b/sys/compat/linuxkpi/common/include/linux/eventfd.h
new file mode 100644
index 000000000000..d167d4b7d189
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/eventfd.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2025 The FreeBSD Foundation
+ * Copyright (c) 2025 Jean-Sébastien Pédron
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LINUXKPI_LINUX_EVENTFD_H_
+#define _LINUXKPI_LINUX_EVENTFD_H_
+
+#include <sys/eventfd.h>
+
+#include <linux/wait.h>
+#include <linux/err.h>
+#include <linux/percpu-defs.h>
+#include <linux/percpu.h>
+#include <linux/sched.h>
+
+/*
+ * Linux uses `struct eventfd_ctx`, but FreeBSD defines `struct eventfd`. Here,
+ * we define a synonym to the FreeBSD structure. This allows to keep Linux code
+ * unmodified.
+ */
+#define eventfd_ctx eventfd
+
+#define eventfd_ctx_fdget lkpi_eventfd_ctx_fdget
+struct eventfd_ctx *lkpi_eventfd_ctx_fdget(int fd);
+
+#define eventfd_ctx_put lkpi_eventfd_ctx_put
+void lkpi_eventfd_ctx_put(struct eventfd_ctx *ctx);
+
+#endif /* _LINUXKPI_LINUX_EVENTFD_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/file.h b/sys/compat/linuxkpi/common/include/linux/file.h
index f6e988c2d88e..be12d5f1bccf 100644
--- a/sys/compat/linuxkpi/common/include/linux/file.h
+++ b/sys/compat/linuxkpi/common/include/linux/file.h
@@ -39,6 +39,11 @@
#include <linux/fs.h>
#include <linux/slab.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/cleanup.h>
+
struct linux_file;
#undef file
diff --git a/sys/compat/linuxkpi/common/include/linux/font.h b/sys/compat/linuxkpi/common/include/linux/font.h
new file mode 100644
index 000000000000..45daa00b61f0
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/font.h
@@ -0,0 +1,33 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ */
+
+#ifndef _LINUXKPI_LINUX_FONT_H_
+#define _LINUXKPI_LINUX_FONT_H_
+
+#include <linux/types.h>
+
+struct font_desc {
+ const char *name;
+ const void *data;
+ int idx;
+ unsigned int width;
+ unsigned int height;
+ unsigned int charcount;
+ int pref;
+};
+
+static inline const struct font_desc *
+get_default_font(int xres, int yres, unsigned long *font_w,
+ unsigned long *font_h)
+{
+ return (NULL);
+}
+
+#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/fs.h b/sys/compat/linuxkpi/common/include/linux/fs.h
index f1568ad6282d..7e28be070850 100644
--- a/sys/compat/linuxkpi/common/include/linux/fs.h
+++ b/sys/compat/linuxkpi/common/include/linux/fs.h
@@ -364,27 +364,23 @@ 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 *p, *read_pos = ((char *) orig) + *ppos;
+ void *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;
+ if (*ppos >= buf_size || read_size == 0)
+ return (0);
if (read_size > buf_remain)
read_size = buf_remain;
- /*
- * 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);
+ /* 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);
}
MALLOC_DECLARE(M_LSATTR);
@@ -415,11 +411,13 @@ int simple_attr_open(struct inode *inode, struct file *filp,
int simple_attr_release(struct inode *inode, struct file *filp);
-ssize_t simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos);
+ssize_t simple_attr_read(struct file *filp, char __user *buf, size_t read_size,
+ loff_t *ppos);
-ssize_t simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t *ppos);
+ssize_t simple_attr_write(struct file *filp, const char __user *buf,
+ size_t write_size, loff_t *ppos);
-ssize_t simple_attr_write_signed(struct file *filp, const char *buf,
+ssize_t simple_attr_write_signed(struct file *filp, const char __user *buf,
size_t write_size, loff_t *ppos);
#endif /* _LINUXKPI_LINUX_FS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/hardirq.h b/sys/compat/linuxkpi/common/include/linux/hardirq.h
index f79451dd0d35..c6cbf1a34f14 100644
--- a/sys/compat/linuxkpi/common/include/linux/hardirq.h
+++ b/sys/compat/linuxkpi/common/include/linux/hardirq.h
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/lockdep.h>
+#include <linux/preempt.h>
#include <sys/param.h>
#include <sys/bus.h>
diff --git a/sys/compat/linuxkpi/common/include/linux/highmem.h b/sys/compat/linuxkpi/common/include/linux/highmem.h
index 58a9cdcdf60f..dc1c4fe2f299 100644
--- a/sys/compat/linuxkpi/common/include/linux/highmem.h
+++ b/sys/compat/linuxkpi/common/include/linux/highmem.h
@@ -45,6 +45,7 @@
#include <linux/mm.h>
#include <linux/page.h>
+#include <linux/hardirq.h>
#define PageHighMem(p) (0)
diff --git a/sys/compat/linuxkpi/common/include/linux/i2c.h b/sys/compat/linuxkpi/common/include/linux/i2c.h
index f24d282586f6..a6a4ee85d584 100644
--- a/sys/compat/linuxkpi/common/include/linux/i2c.h
+++ b/sys/compat/linuxkpi/common/include/linux/i2c.h
@@ -31,7 +31,14 @@
#include <sys/errno.h>
#include <sys/systm.h>
+#include <linux/bits.h>
+#include <linux/mod_devicetable.h>
#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
#define I2C_MAX_ADAPTER_NAME_LENGTH 32
diff --git a/sys/compat/linuxkpi/common/include/linux/ieee80211.h b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
index 12160df43915..d1eba94a3ad8 100644
--- a/sys/compat/linuxkpi/common/include/linux/ieee80211.h
+++ b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -51,8 +51,16 @@ extern int linuxkpi_debug_80211;
#define IMPROVE(fmt, ...) if (linuxkpi_debug_80211 & D80211_IMPROVE) \
printf("%s:%d: XXX LKPI80211 IMPROVE " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
-
-/* 9.4.2.55 Management MIC element (CMAC-256, GMAC-128, and GMAC-256). */
+/* 802.11-2024, 9.4.2.53 MME. */
+/* BIP-CMAC-128 */
+struct ieee80211_mmie {
+ uint8_t element_id;
+ uint8_t length;
+ uint16_t key_id;
+ uint8_t ipn[6];
+ uint8_t mic[8];
+};
+/* BIP-CMAC-256, BIP-GMAC-128, BIP-GMAC-256 */
struct ieee80211_mmie_16 {
uint8_t element_id;
uint8_t length;
@@ -108,7 +116,18 @@ struct ieee80211_mmie_16 {
#define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100
enum ieee80211_rate_flags {
- IEEE80211_RATE_SHORT_PREAMBLE = BIT(0),
+ IEEE80211_RATE_SHORT_PREAMBLE = BIT(0), /* 2.4Ghz, CCK */
+ IEEE80211_RATE_SUPPORTS_5MHZ = BIT(1),
+ IEEE80211_RATE_SUPPORTS_10MHZ = BIT(2),
+ IEEE80211_RATE_ERP_G = BIT(3),
+
+ /*
+ * According to documentation these are flags initialized internally.
+ * See lkpi_wiphy_band_annotate().
+ */
+ IEEE80211_RATE_MANDATORY_A = BIT(4),
+ IEEE80211_RATE_MANDATORY_G = BIT(5),
+ IEEE80211_RATE_MANDATORY_B = BIT(6),
};
enum ieee80211_rate_control_changed_flags {
@@ -200,6 +219,7 @@ enum ieee80211_min_mpdu_start_spacing {
#define IEEE80211_FCTL_TODS (IEEE80211_FC1_DIR_TODS << 8)
#define IEEE80211_FCTL_MOREFRAGS (IEEE80211_FC1_MORE_FRAG << 8)
#define IEEE80211_FCTL_PM (IEEE80211_FC1_PWR_MGT << 8)
+#define IEEE80211_FCTL_MOREDATA (IEEE80211_FC1_MORE_DATA << 8)
#define IEEE80211_FTYPE_MGMT IEEE80211_FC0_TYPE_MGT
#define IEEE80211_FTYPE_CTL IEEE80211_FC0_TYPE_CTL
@@ -461,18 +481,6 @@ enum ieee80211_tx_control_flags {
IEEE80211_TX_CTRL_MLO_LINK = 0xF0000000, /* This is IEEE80211_LINK_UNSPECIFIED on the high bits. */
};
-enum ieee80211_tx_rate_flags {
- /* XXX TODO .. right shift numbers */
- IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(0),
- IEEE80211_TX_RC_80_MHZ_WIDTH = BIT(1),
- IEEE80211_TX_RC_160_MHZ_WIDTH = BIT(2),
- IEEE80211_TX_RC_GREEN_FIELD = BIT(3),
- IEEE80211_TX_RC_MCS = BIT(4),
- IEEE80211_TX_RC_SHORT_GI = BIT(5),
- IEEE80211_TX_RC_VHT_MCS = BIT(6),
- IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(7),
-};
-
#define IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED -128
#define IEEE80211_HT_CTL_LEN 4
diff --git a/sys/compat/linuxkpi/common/include/linux/kfifo.h b/sys/compat/linuxkpi/common/include/linux/kfifo.h
index d2f570781661..fbe16e22683e 100644
--- a/sys/compat/linuxkpi/common/include/linux/kfifo.h
+++ b/sys/compat/linuxkpi/common/include/linux/kfifo.h
@@ -33,8 +33,26 @@
#include <linux/slab.h>
#include <linux/gfp.h>
-#define INIT_KFIFO(x) 0
-#define DECLARE_KFIFO(x, y, z)
+/*
+ * INIT_KFIFO() is used to initialize the structure declared with
+ * DECLARE_KFIFO(). It doesn't work with DECLARE_KFIFO_PTR().
+ */
+#define INIT_KFIFO(_kf) \
+ ({ \
+ (_kf).total = nitems((_kf).head); \
+ (_kf).count = 0; \
+ (_kf).first = 0; \
+ (_kf).last = 0; \
+ })
+
+#define DECLARE_KFIFO(_name, _type, _size) \
+ struct kfifo_ ## _name { \
+ size_t total; \
+ size_t count; \
+ size_t first; \
+ size_t last; \
+ _type head[_size]; \
+ } _name
#define DECLARE_KFIFO_PTR(_name, _type) \
struct kfifo_ ## _name { \
diff --git a/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h b/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h
new file mode 100644
index 000000000000..25f96b304f59
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h
@@ -0,0 +1,51 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ */
+
+#ifndef _LINUXKPI_LINUX_KMSG_DUMP_H_
+#define _LINUXKPI_LINUX_KMSG_DUMP_H_
+
+#include <linux/errno.h>
+#include <linux/list.h>
+
+#include <linux/kernel.h> /* For pr_debug() */
+
+enum kmsg_dump_reason {
+ KMSG_DUMP_UNDEF,
+ KMSG_DUMP_PANIC,
+ KMSG_DUMP_OOPS,
+ KMSG_DUMP_EMERG,
+ KMSG_DUMP_SHUTDOWN,
+ KMSG_DUMP_MAX
+};
+
+struct kmsg_dumper {
+ struct list_head list;
+ void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason);
+ enum kmsg_dump_reason max_reason;
+ bool registered;
+};
+
+static inline int
+kmsg_dump_register(struct kmsg_dumper *dumper)
+{
+ pr_debug("TODO");
+
+ return (-EINVAL);
+}
+
+static inline int
+kmsg_dump_unregister(struct kmsg_dumper *dumper)
+{
+ pr_debug("TODO");
+
+ return (-EINVAL);
+}
+
+#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/kstrtox.h b/sys/compat/linuxkpi/common/include/linux/kstrtox.h
index 5da99de24197..05bf94dd375d 100644
--- a/sys/compat/linuxkpi/common/include/linux/kstrtox.h
+++ b/sys/compat/linuxkpi/common/include/linux/kstrtox.h
@@ -74,14 +74,17 @@ static inline int
kstrtoul(const char *cp, unsigned int base, unsigned long *res)
{
char *end;
+ unsigned long temp;
- *res = strtoul(cp, &end, base);
+ temp = strtoul(cp, &end, base);
/* skip newline character, if any */
if (*end == '\n')
end++;
if (*cp == 0 || *end != 0)
return (-EINVAL);
+
+ *res = temp;
return (0);
}
@@ -89,14 +92,17 @@ static inline int
kstrtol(const char *cp, unsigned int base, long *res)
{
char *end;
+ long temp;
- *res = strtol(cp, &end, base);
+ temp = strtol(cp, &end, base);
/* skip newline character, if any */
if (*end == '\n')
end++;
if (*cp == 0 || *end != 0)
return (-EINVAL);
+
+ *res = temp;
return (0);
}
@@ -106,7 +112,7 @@ kstrtoint(const char *cp, unsigned int base, int *res)
char *end;
long temp;
- *res = temp = strtol(cp, &end, base);
+ temp = strtol(cp, &end, base);
/* skip newline character, if any */
if (*end == '\n')
@@ -115,6 +121,8 @@ kstrtoint(const char *cp, unsigned int base, int *res)
return (-EINVAL);
if (temp != (int)temp)
return (-ERANGE);
+
+ *res = (int)temp;
return (0);
}
@@ -124,7 +132,7 @@ kstrtouint(const char *cp, unsigned int base, unsigned int *res)
char *end;
unsigned long temp;
- *res = temp = strtoul(cp, &end, base);
+ temp = strtoul(cp, &end, base);
/* skip newline character, if any */
if (*end == '\n')
@@ -133,6 +141,8 @@ kstrtouint(const char *cp, unsigned int base, unsigned int *res)
return (-EINVAL);
if (temp != (unsigned int)temp)
return (-ERANGE);
+
+ *res = (unsigned int)temp;
return (0);
}
@@ -142,7 +152,7 @@ kstrtou8(const char *cp, unsigned int base, uint8_t *res)
char *end;
unsigned long temp;
- *res = temp = strtoul(cp, &end, base);
+ temp = strtoul(cp, &end, base);
/* skip newline character, if any */
if (*end == '\n')
@@ -151,6 +161,8 @@ kstrtou8(const char *cp, unsigned int base, uint8_t *res)
return (-EINVAL);
if (temp != (uint8_t)temp)
return (-ERANGE);
+
+ *res = (uint8_t)temp;
return (0);
}
@@ -160,7 +172,7 @@ kstrtou16(const char *cp, unsigned int base, uint16_t *res)
char *end;
unsigned long temp;
- *res = temp = strtoul(cp, &end, base);
+ temp = strtoul(cp, &end, base);
/* skip newline character, if any */
if (*end == '\n')
@@ -169,20 +181,20 @@ kstrtou16(const char *cp, unsigned int base, uint16_t *res)
return (-EINVAL);
if (temp != (uint16_t)temp)
return (-ERANGE);
+
+ *res = (uint16_t)temp;
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));
}
@@ -190,14 +202,17 @@ static inline int
kstrtos64(const char *cp, unsigned int base, int64_t *res)
{
char *end;
+ quad_t temp;
- *res = strtoq(cp, &end, base);
+ temp = strtoq(cp, &end, base);
/* skip newline character, if any */
if (*end == '\n')
end++;
if (*cp == 0 || *end != 0)
return (-EINVAL);
+
+ *res = (int64_t)temp;
return (0);
}
@@ -208,17 +223,20 @@ kstrtoll(const char *cp, unsigned int base, long long *res)
}
static inline int
-kstrtou64(const char *cp, unsigned int base, u64 *res)
+kstrtou64(const char *cp, unsigned int base, uint64_t *res)
{
char *end;
+ u_quad_t temp;
- *res = strtouq(cp, &end, base);
+ temp = strtouq(cp, &end, base);
/* skip newline character, if any */
if (*end == '\n')
end++;
if (*cp == 0 || *end != 0)
return (-EINVAL);
+
+ *res = (uint64_t)temp;
return (0);
}
@@ -231,22 +249,16 @@ kstrtoull(const char *cp, unsigned int base, unsigned long long *res)
static inline int
kstrtobool(const char *s, bool *res)
{
- int len;
-
- if (s == NULL || (len = strlen(s)) == 0 || res == NULL)
+ if (s == NULL || *s == '\0')
return (-EINVAL);
- /* skip newline character, if any */
- if (s[len - 1] == '\n')
- len--;
-
- if (len == 1 && strchr("yY1", s[0]) != NULL)
+ if (strchr("eEtTyY1", s[0]) != NULL)
*res = true;
- else if (len == 1 && strchr("nN0", s[0]) != NULL)
+ else if (strchr("dDfFnN0", s[0]) != NULL)
*res = false;
- else if (strncasecmp("on", s, len) == 0)
+ else if (strncasecmp("on", s, 2) == 0)
*res = true;
- else if (strncasecmp("off", s, len) == 0)
+ else if (strncasecmp("of", s, 2) == 0)
*res = false;
else
return (-EINVAL);
@@ -302,7 +314,6 @@ 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));
}
diff --git a/sys/compat/linuxkpi/common/include/linux/memcontrol.h b/sys/compat/linuxkpi/common/include/linux/memcontrol.h
new file mode 100644
index 000000000000..57fadf9af60f
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/memcontrol.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2025 The FreeBSD Foundation
+ * Copyright (c) 2025 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef _LINUXKPI_LINUX_MEMCONTROL_H_
+#define _LINUXKPI_LINUX_MEMCONTROL_H_
+
+#include <linux/cgroup.h>
+#include <linux/hardirq.h>
+#include <linux/jump_label.h>
+#include <linux/kernel.h>
+#include <linux/eventfd.h>
+#include <linux/mm.h>
+#include <linux/page-flags.h>
+#include <linux/shrinker.h>
+
+#endif /* defined(_LINUXKPI_LINUX_MEMCONTROL_H_) */
diff --git a/sys/compat/linuxkpi/common/include/linux/minmax.h b/sys/compat/linuxkpi/common/include/linux/minmax.h
index d48958f0899f..fb8eb6f704b4 100644
--- a/sys/compat/linuxkpi/common/include/linux/minmax.h
+++ b/sys/compat/linuxkpi/common/include/linux/minmax.h
@@ -71,4 +71,7 @@
b = _swap_tmp; \
} while (0)
+/* XXX would have to make sure both are unsigned. */
+#define umin(x, y) MIN(x, y)
+
#endif /* _LINUXKPI_LINUX_MINMAX_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h b/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h
index 87bd6ec24bce..df7c2472861b 100644
--- a/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h
+++ b/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h
@@ -80,4 +80,10 @@ struct dmi_system_id {
#define ACPI_ID_LEN 16
+/* -----------------------------------------------------------------------------
+ * USB
+ */
+/* struct usb_device_id is defined in sys/dev/usb/usbdi.h. */
+/* MODULE_DEVICE_TABLE_BUS_usb we have in usb.h. */
+
#endif /* __LINUXKPI_LINUX_MOD_DEVICETABLE_H__ */
diff --git a/sys/compat/linuxkpi/common/include/linux/module.h b/sys/compat/linuxkpi/common/include/linux/module.h
index 079dacf8df6c..fbe57cbbed82 100644
--- a/sys/compat/linuxkpi/common/include/linux/module.h
+++ b/sys/compat/linuxkpi/common/include/linux/module.h
@@ -53,6 +53,24 @@
#define MODULE_SUPPORTED_DEVICE(name)
#define MODULE_IMPORT_NS(_name)
+/* Linux has an empty element at the end of the ID table -> nitems() - 1. */
+#define MODULE_DEVICE_TABLE(_bus, _table) \
+ \
+static device_method_t _ ## _bus ## _ ## _table ## _methods[] = { \
+ DEVMETHOD_END \
+}; \
+ \
+static driver_t _ ## _bus ## _ ## _table ## _driver = { \
+ "lkpi_" #_bus #_table, \
+ _ ## _bus ## _ ## _table ## _methods, \
+ 0 \
+}; \
+ \
+DRIVER_MODULE(lkpi_ ## _table, _bus, _ ## _bus ## _ ## _table ## _driver,\
+ 0, 0); \
+ \
+MODULE_DEVICE_TABLE_BUS_ ## _bus(_bus, _table)
+
/*
* THIS_MODULE is used to differentiate modules on Linux. We currently
* completely stub out any Linux struct module usage, but THIS_MODULE is still
diff --git a/sys/compat/linuxkpi/common/include/linux/netdevice.h b/sys/compat/linuxkpi/common/include/linux/netdevice.h
index cf27753bcb80..dfed5fbd61b4 100644
--- a/sys/compat/linuxkpi/common/include/linux/netdevice.h
+++ b/sys/compat/linuxkpi/common/include/linux/netdevice.h
@@ -160,6 +160,30 @@ struct net_device {
#define SET_NETDEV_DEV(_ndev, _dev) (_ndev)->dev.parent = _dev;
+enum net_device_path_type {
+ DEV_PATH_MTK_WDMA,
+};
+
+struct net_device_path {
+ enum net_device_path_type type;
+ const struct net_device *dev;
+ /* We assume there's a struct per type. */
+ union {
+ struct {
+ uint16_t wcid;
+ uint8_t wdma_idx;
+ uint8_t queue;
+ uint8_t bss;
+ uint8_t amsdu;
+ } mtk_wdma;
+ };
+};
+
+struct net_device_path_ctx {
+ const struct net_device *dev;
+};
+
+
/* -------------------------------------------------------------------------- */
/* According to linux::ipoib_main.c. */
struct netdev_notifier_info {
diff --git a/sys/compat/linuxkpi/common/include/linux/notifier.h b/sys/compat/linuxkpi/common/include/linux/notifier.h
index 9302a1ce4606..4fe43255c648 100644
--- a/sys/compat/linuxkpi/common/include/linux/notifier.h
+++ b/sys/compat/linuxkpi/common/include/linux/notifier.h
@@ -32,6 +32,11 @@
#include <sys/types.h>
#include <sys/eventhandler.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/srcu.h>
+
#define NOTIFY_DONE 0
#define NOTIFY_OK 0x0001
#define NOTIFY_STOP_MASK 0x8000
diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h
index 06336bf963d6..ccbd425de5da 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci.h
@@ -76,24 +76,6 @@ struct pci_device_id {
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) \
- \
-static device_method_t _ ## _bus ## _ ## _table ## _methods[] = { \
- DEVMETHOD_END \
-}; \
- \
-static driver_t _ ## _bus ## _ ## _table ## _driver = { \
- "lkpi_" #_bus #_table, \
- _ ## _bus ## _ ## _table ## _methods, \
- 0 \
-}; \
- \
-DRIVER_MODULE(lkpi_ ## _table, _bus, _ ## _bus ## _ ## _table ## _driver,\
- 0, 0); \
- \
-MODULE_DEVICE_TABLE_BUS_ ## _bus(_bus, _table)
-
#define PCI_ANY_ID -1U
#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
@@ -253,6 +235,20 @@ extern const char *pci_power_names[6];
#define PCI_IRQ_LEGACY PCI_IRQ_INTX
#endif
+/*
+ * Linux PCI code uses `PCI_SET_ERROR_RESPONSE()` to indicate to the caller of
+ * a `pci_read_*()` function that the read failed. An example of failure is
+ * whether the device was disconnected. It is a bit weird because Linux
+ * `pci_read_*()` can return an error value, as the read value is stored in a
+ * integer passed by pointer.
+ *
+ * We don't set PCI_ERROR_RESPONSE anywhere as of this commit, but the DRM
+ * drivers started to use `PCI_POSSIBLE_ERROR()`.
+ */
+#define PCI_ERROR_RESPONSE (~0ULL)
+#define PCI_SET_ERROR_RESPONSE(val) (*(val) = ((typeof(*(val))) PCI_ERROR_RESPONSE))
+#define PCI_POSSIBLE_ERROR(val) ((val) == ((typeof(val)) PCI_ERROR_RESPONSE))
+
struct pci_dev;
struct pci_driver {
@@ -1122,19 +1118,28 @@ pci_num_vf(struct pci_dev *dev)
static inline enum pci_bus_speed
pcie_get_speed_cap(struct pci_dev *dev)
{
+ struct pci_dev *pbus;
device_t root;
uint32_t lnkcap, lnkcap2;
int error, pos;
- root = device_get_parent(dev->dev.bsddev);
- if (root == NULL)
- return (PCI_SPEED_UNKNOWN);
- root = device_get_parent(root);
- if (root == NULL)
- return (PCI_SPEED_UNKNOWN);
- root = device_get_parent(root);
- if (root == NULL)
- return (PCI_SPEED_UNKNOWN);
+ /*
+ * We should always be called on a PCI device.
+ * The only current consumer I could find was amdgpu which either
+ * calls us directly on a pdev(drmn?) or with the result of
+ * pci_upstream_bridge().
+ *
+ * Treat "drmn" as special again as it is not a PCI device.
+ */
+ if (dev->pdrv != NULL && dev->pdrv->isdrm) {
+ pbus = pci_upstream_bridge(dev);
+ if (pbus == NULL)
+ return (PCI_SPEED_UNKNOWN);
+ } else
+ pbus = dev;
+
+ /* "root" may be misleading as it may not be that. */
+ root = pbus->dev.bsddev;
if (pci_get_vendor(root) == PCI_VENDOR_ID_VIA ||
pci_get_vendor(root) == PCI_VENDOR_ID_SERVERWORKS)
diff --git a/sys/compat/linuxkpi/common/include/linux/platform_data/brcmfmac.h b/sys/compat/linuxkpi/common/include/linux/platform_data/brcmfmac.h
new file mode 100644
index 000000000000..ec99b7b73d1d
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/platform_data/brcmfmac.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2016 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LINUX_BRCMFMAC_PLATFORM_H
+#define _LINUX_BRCMFMAC_PLATFORM_H
+
+
+#define BRCMFMAC_PDATA_NAME "brcmfmac"
+
+#define BRCMFMAC_COUNTRY_BUF_SZ 4
+
+
+/*
+ * Platform specific driver functions and data. Through the platform specific
+ * device data functions and data can be provided to help the brcmfmac driver to
+ * operate with the device in combination with the used platform.
+ */
+
+
+/**
+ * Note: the brcmfmac can be loaded as module or be statically built-in into
+ * the kernel. If built-in then do note that it uses module_init (and
+ * module_exit) routines which equal device_initcall. So if you intend to
+ * create a module with the platform specific data for the brcmfmac and have
+ * it built-in to the kernel then use a higher initcall then device_initcall
+ * (see init.h). If this is not done then brcmfmac will load without problems
+ * but will not pickup the platform data.
+ *
+ * When the driver does not "detect" platform driver data then it will continue
+ * without reporting anything and just assume there is no data needed. Which is
+ * probably true for most platforms.
+ */
+
+/**
+ * enum brcmf_bus_type - Bus type identifier. Currently SDIO, USB and PCIE are
+ * supported.
+ */
+enum brcmf_bus_type {
+ BRCMF_BUSTYPE_SDIO,
+ BRCMF_BUSTYPE_USB,
+ BRCMF_BUSTYPE_PCIE
+};
+
+
+/**
+ * struct brcmfmac_sdio_pd - SDIO Device specific platform data.
+ *
+ * @txglomsz: SDIO txglom size. Use 0 if default of driver is to be
+ * used.
+ * @drive_strength: is the preferred drive_strength to be used for the SDIO
+ * pins. If 0 then a default value will be used. This is
+ * the target drive strength, the exact drive strength
+ * which will be used depends on the capabilities of the
+ * device.
+ * @oob_irq_supported: does the board have support for OOB interrupts. SDIO
+ * in-band interrupts are relatively slow and for having
+ * less overhead on interrupt processing an out of band
+ * interrupt can be used. If the HW supports this then
+ * enable this by setting this field to true and configure
+ * the oob related fields.
+ * @oob_irq_nr,
+ * @oob_irq_flags: the OOB interrupt information. The values are used for
+ * registering the irq using request_irq function.
+ * @broken_sg_support: flag for broken sg list support of SDIO host controller.
+ * Set this to true if the SDIO host controller has higher
+ * align requirement than 32 bytes for each scatterlist
+ * item.
+ * @sd_head_align: alignment requirement for start of data buffer.
+ * @sd_sgentry_align: length alignment requirement for each sg entry.
+ * @reset: This function can get called if the device communication
+ * broke down. This functionality is particularly useful in
+ * case of SDIO type devices. It is possible to reset a
+ * dongle via sdio data interface, but it requires that
+ * this is fully functional. This function is chip/module
+ * specific and this function should return only after the
+ * complete reset has completed.
+ */
+struct brcmfmac_sdio_pd {
+ int txglomsz;
+ unsigned int drive_strength;
+ bool oob_irq_supported;
+ unsigned int oob_irq_nr;
+ unsigned long oob_irq_flags;
+ bool broken_sg_support;
+ unsigned short sd_head_align;
+ unsigned short sd_sgentry_align;
+ void (*reset)(void);
+};
+
+/**
+ * struct brcmfmac_pd_cc_entry - Struct for translating user space country code
+ * (iso3166) to firmware country code and
+ * revision.
+ *
+ * @iso3166: iso3166 alpha 2 country code string.
+ * @cc: firmware country code string.
+ * @rev: firmware country code revision.
+ */
+struct brcmfmac_pd_cc_entry {
+ char iso3166[BRCMFMAC_COUNTRY_BUF_SZ];
+ char cc[BRCMFMAC_COUNTRY_BUF_SZ];
+ s32 rev;
+};
+
+/**
+ * struct brcmfmac_pd_cc - Struct for translating country codes as set by user
+ * space to a country code and rev which can be used by
+ * firmware.
+ *
+ * @table_size: number of entries in table (> 0)
+ * @table: array of 1 or more elements with translation information.
+ */
+struct brcmfmac_pd_cc {
+ int table_size;
+ struct brcmfmac_pd_cc_entry table[];
+};
+
+/**
+ * struct brcmfmac_pd_device - Device specific platform data. (id/rev/bus_type)
+ * is the unique identifier of the device.
+ *
+ * @id: ID of the device for which this data is. In case of SDIO
+ * or PCIE this is the chipid as identified by chip.c In
+ * case of USB this is the chipid as identified by the
+ * device query.
+ * @rev: chip revision, see id.
+ * @bus_type: The type of bus. Some chipid/rev exist for different bus
+ * types. Each bus type has its own set of settings.
+ * @feature_disable: Bitmask of features to disable (override), See feature.c
+ * in brcmfmac for details.
+ * @country_codes: If available, pointer to struct for translating country
+ * codes.
+ * @bus: Bus specific (union) device settings. Currently only
+ * SDIO.
+ */
+struct brcmfmac_pd_device {
+ unsigned int id;
+ unsigned int rev;
+ enum brcmf_bus_type bus_type;
+ unsigned int feature_disable;
+ struct brcmfmac_pd_cc *country_codes;
+ union {
+ struct brcmfmac_sdio_pd sdio;
+ } bus;
+};
+
+/**
+ * struct brcmfmac_platform_data - BRCMFMAC specific platform data.
+ *
+ * @power_on: This function is called by the brcmfmac driver when the module
+ * gets loaded. This can be particularly useful for low power
+ * devices. The platform spcific routine may for example decide to
+ * power up the complete device. If there is no use-case for this
+ * function then provide NULL.
+ * @power_off: This function is called by the brcmfmac when the module gets
+ * unloaded. At this point the devices can be powered down or
+ * otherwise be reset. So if an actual power_off is not supported
+ * but reset is supported by the devices then reset the devices
+ * when this function gets called. This can be particularly useful
+ * for low power devices. If there is no use-case for this
+ * function then provide NULL.
+ */
+struct brcmfmac_platform_data {
+ void (*power_on)(void);
+ void (*power_off)(void);
+ char *fw_alternative_path;
+ int device_count;
+ struct brcmfmac_pd_device devices[];
+};
+
+
+#endif /* _LINUX_BRCMFMAC_PLATFORM_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/printk.h b/sys/compat/linuxkpi/common/include/linux/printk.h
index d2d197682782..066ec900f04e 100644
--- a/sys/compat/linuxkpi/common/include/linux/printk.h
+++ b/sys/compat/linuxkpi/common/include/linux/printk.h
@@ -48,7 +48,13 @@ 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);
+ const void *, size_t, const bool, const bool);
+
+#define hex_dump_to_buffer(buf, len, rowsize, groupsize, linebuf, linebuflen, ascii) \
+ lkpi_hex_dump_to_buffer((buf), (len), (rowsize), (groupsize), (linebuf), (linebuflen), (ascii))
+
+int lkpi_hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
+ int groupsize, char *linebuf, size_t linebuflen, bool ascii);
static inline void
print_hex_dump(const char *level, const char *prefix_str,
@@ -56,7 +62,7 @@ print_hex_dump(const char *level, const char *prefix_str,
const void *buf, size_t len, const bool ascii)
{
lkpi_hex_dump(__lkpi_hexdump_printf, NULL, level, prefix_str, prefix_type,
- rowsize, groupsize, buf, len, ascii);
+ rowsize, groupsize, buf, len, ascii, true);
}
static inline void
diff --git a/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h b/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h
index aad46cc25b1b..6491cbeab7e2 100644
--- a/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h
+++ b/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h
@@ -49,6 +49,7 @@ struct ptp_clock_info {
int (*adjtime)(struct ptp_clock_info *, s64);
int (*getcrosststamp)(struct ptp_clock_info *, struct system_device_crosststamp *);
int (*gettime64)(struct ptp_clock_info *, struct timespec *);
+ int (*settime64)(struct ptp_clock_info *, const struct timespec *);
};
static inline struct ptp_clock *
diff --git a/sys/compat/linuxkpi/common/include/linux/radix-tree.h b/sys/compat/linuxkpi/common/include/linux/radix-tree.h
index 1019697303db..7182f4a9e407 100644
--- a/sys/compat/linuxkpi/common/include/linux/radix-tree.h
+++ b/sys/compat/linuxkpi/common/include/linux/radix-tree.h
@@ -38,12 +38,19 @@
#define RADIX_TREE_MAX_HEIGHT \
howmany(sizeof(long) * NBBY, RADIX_TREE_MAP_SHIFT)
+#define RADIX_TREE_MAX_TAGS 3
+#define RADIX_TREE_TAG_LONGS RADIX_TREE_MAP_SIZE
+
#define RADIX_TREE_ENTRY_MASK 3UL
#define RADIX_TREE_EXCEPTIONAL_ENTRY 2UL
#define RADIX_TREE_EXCEPTIONAL_SHIFT 2
+#define RADIX_TREE_ITER_TAG_MASK 0x0f
+#define RADIX_TREE_ITER_TAGGED 0x10
+
struct radix_tree_node {
void *slots[RADIX_TREE_MAP_SIZE];
+ unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
int count;
};
@@ -51,6 +58,8 @@ struct radix_tree_root {
struct radix_tree_node *rnode;
gfp_t gfp_mask;
int height;
+ /* Linux stores root tags inside `gfp_mask`. */
+ unsigned long tags[RADIX_TREE_MAX_TAGS];
};
struct radix_tree_iter {
@@ -64,9 +73,22 @@ struct radix_tree_iter {
#define RADIX_TREE(name, mask) \
struct radix_tree_root name = RADIX_TREE_INIT(mask)
-#define radix_tree_for_each_slot(slot, root, iter, start) \
- for ((iter)->index = (start); \
- radix_tree_iter_find(root, iter, &(slot)); (iter)->index++)
+#define radix_tree_for_each_slot(slot, root, iter, start) \
+ for ((iter)->index = (start); \
+ radix_tree_iter_find(root, iter, &(slot), 0); \
+ (iter)->index++)
+
+#define radix_tree_for_each_slot_tagged(slot, root, iter, start, tag) \
+ for ((iter)->index = (start); \
+ radix_tree_iter_find(root, iter, &(slot), \
+ RADIX_TREE_ITER_TAGGED | tag); \
+ (iter)->index++)
+
+static inline void *
+radix_tree_deref_slot(void **slot)
+{
+ return (*slot);
+}
static inline int
radix_tree_exception(void *arg)
@@ -78,7 +100,12 @@ void *radix_tree_lookup(const struct radix_tree_root *, unsigned long);
void *radix_tree_delete(struct radix_tree_root *, unsigned long);
int radix_tree_insert(struct radix_tree_root *, unsigned long, void *);
int radix_tree_store(struct radix_tree_root *, unsigned long, void **);
-bool radix_tree_iter_find(const struct radix_tree_root *, struct radix_tree_iter *, void ***);
+bool radix_tree_iter_find(const struct radix_tree_root *, struct radix_tree_iter *, void ***, int);
void radix_tree_iter_delete(struct radix_tree_root *, struct radix_tree_iter *, void **);
+void *radix_tree_tag_set(struct radix_tree_root *root, unsigned long index, unsigned int tag);
+void *radix_tree_tag_clear(struct radix_tree_root *root, unsigned long index, unsigned int tag);
+int radix_tree_tagged(const struct radix_tree_root *root, unsigned int tag);
+unsigned int radix_tree_gang_lookup(const struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items);
+unsigned int radix_tree_gang_lookup_tag(const struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag);
#endif /* _LINUXKPI_LINUX_RADIX_TREE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/ratelimit.h b/sys/compat/linuxkpi/common/include/linux/ratelimit.h
index 9585b4b994d7..fdef57c7882d 100644
--- a/sys/compat/linuxkpi/common/include/linux/ratelimit.h
+++ b/sys/compat/linuxkpi/common/include/linux/ratelimit.h
@@ -14,4 +14,11 @@ struct ratelimit_state {
#define ratelimit_state_init(x, y, z)
#define ratelimit_set_flags(x, y)
+#define WARN_RATELIMIT(condition, ...) ({ \
+ bool __ret_warn_on = (condition); \
+ if (unlikely(__ret_warn_on)) \
+ pr_warn_ratelimited(__VA_ARGS__); \
+ unlikely(__ret_warn_on); \
+})
+
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/regulator/consumer.h b/sys/compat/linuxkpi/common/include/linux/regulator/consumer.h
new file mode 100644
index 000000000000..d6c23575bc83
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/regulator/consumer.h
@@ -0,0 +1,17 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ */
+
+#ifndef _LINUXKPI_LINUX_REGULATOR_CONSUMER_H_
+#define _LINUXKPI_LINUX_REGULATOR_CONSUMER_H_
+
+#include <linux/err.h>
+#include <linux/suspend.h>
+
+#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/seq_buf.h b/sys/compat/linuxkpi/common/include/linux/seq_buf.h
new file mode 100644
index 000000000000..d6246a40e6f7
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/seq_buf.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_SEQ_BUF_H_
+#define _LINUXKPI_LINUX_SEQ_BUF_H_
+
+#include <linux/bug.h>
+#include <linux/minmax.h>
+#include <linux/seq_file.h>
+#include <linux/types.h>
+
+struct seq_buf {
+ char *buffer;
+ size_t size;
+ size_t len;
+};
+
+#define DECLARE_SEQ_BUF(NAME, SIZE) \
+ struct seq_buf NAME = { \
+ .buffer = (char[SIZE]) { 0 }, \
+ .size = SIZE, \
+ }
+
+static inline void
+seq_buf_clear(struct seq_buf *s)
+{
+ s->len = 0;
+ if (s->size > 0)
+ s->buffer[0] = '\0';
+}
+
+static inline void
+seq_buf_set_overflow(struct seq_buf *s)
+{
+ s->len = s->size + 1;
+}
+
+static inline bool
+seq_buf_has_overflowed(struct seq_buf *s)
+{
+ return (s->len > s->size);
+}
+
+static inline bool
+seq_buf_buffer_left(struct seq_buf *s)
+{
+ if (seq_buf_has_overflowed(s))
+ return (0);
+
+ return (s->size - s->len);
+}
+
+#define seq_buf_init(s, buf, size) linuxkpi_seq_buf_init((s), (buf), (size))
+void linuxkpi_seq_buf_init(struct seq_buf *s, char *buf, unsigned int size);
+
+#define seq_buf_printf(s, f, ...) linuxkpi_seq_buf_printf((s), (f), __VA_ARGS__)
+int linuxkpi_seq_buf_printf(struct seq_buf *s, const char *fmt, ...) \
+ __printflike(2, 3);
+
+#define seq_buf_vprintf(s, f, a) linuxkpi_seq_buf_vprintf((s), (f), (a))
+int linuxkpi_seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args);
+
+#define seq_buf_str(s) linuxkpi_seq_buf_str((s))
+const char * linuxkpi_seq_buf_str(struct seq_buf *s);
+
+#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/seq_file.h b/sys/compat/linuxkpi/common/include/linux/seq_file.h
index 47da16ab8688..786c09bd6a20 100644
--- a/sys/compat/linuxkpi/common/include/linux/seq_file.h
+++ b/sys/compat/linuxkpi/common/include/linux/seq_file.h
@@ -85,7 +85,7 @@ struct seq_operations {
int (*show) (struct seq_file *m, void *v);
};
-ssize_t seq_read(struct linux_file *, char *, size_t, off_t *);
+ssize_t seq_read(struct linux_file *, char __user *, size_t, off_t *);
int seq_write(struct seq_file *seq, const void *data, size_t len);
void seq_putc(struct seq_file *m, char c);
void seq_puts(struct seq_file *m, const char *str);
@@ -115,7 +115,7 @@ 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);
+ rowsize, groupsize, buf, len, ascii, true);
}
#define file linux_file
diff --git a/sys/compat/linuxkpi/common/include/linux/siphash.h b/sys/compat/linuxkpi/common/include/linux/siphash.h
new file mode 100644
index 000000000000..9153e77382e1
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/siphash.h
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/* Copyright (C) 2016-2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * SipHash: a fast short-input PRF
+ * https://131002.net/siphash/
+ *
+ * This implementation is specifically for SipHash2-4 for a secure PRF
+ * and HalfSipHash1-3/SipHash1-3 for an insecure PRF only suitable for
+ * hashtables.
+ */
+
+#ifndef _LINUX_SIPHASH_H
+#define _LINUX_SIPHASH_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#define SIPHASH_ALIGNMENT __alignof__(u64)
+typedef struct {
+ u64 key[2];
+} siphash_key_t;
+
+#define siphash_aligned_key_t siphash_key_t __aligned(16)
+
+static inline bool siphash_key_is_zero(const siphash_key_t *key)
+{
+ return !(key->key[0] | key->key[1]);
+}
+
+u64 __siphash_aligned(const void *data, size_t len, const siphash_key_t *key);
+u64 __siphash_unaligned(const void *data, size_t len, const siphash_key_t *key);
+
+u64 siphash_1u64(const u64 a, const siphash_key_t *key);
+u64 siphash_2u64(const u64 a, const u64 b, const siphash_key_t *key);
+u64 siphash_3u64(const u64 a, const u64 b, const u64 c,
+ const siphash_key_t *key);
+u64 siphash_4u64(const u64 a, const u64 b, const u64 c, const u64 d,
+ const siphash_key_t *key);
+u64 siphash_1u32(const u32 a, const siphash_key_t *key);
+u64 siphash_3u32(const u32 a, const u32 b, const u32 c,
+ const siphash_key_t *key);
+
+static inline u64 siphash_2u32(const u32 a, const u32 b,
+ const siphash_key_t *key)
+{
+ return siphash_1u64((u64)b << 32 | a, key);
+}
+static inline u64 siphash_4u32(const u32 a, const u32 b, const u32 c,
+ const u32 d, const siphash_key_t *key)
+{
+ return siphash_2u64((u64)b << 32 | a, (u64)d << 32 | c, key);
+}
+
+
+static inline u64 ___siphash_aligned(const __le64 *data, size_t len,
+ const siphash_key_t *key)
+{
+ if (__builtin_constant_p(len) && len == 4)
+ return siphash_1u32(le32_to_cpup((const __le32 *)data), key);
+ if (__builtin_constant_p(len) && len == 8)
+ return siphash_1u64(le64_to_cpu(data[0]), key);
+ if (__builtin_constant_p(len) && len == 16)
+ return siphash_2u64(le64_to_cpu(data[0]), le64_to_cpu(data[1]),
+ key);
+ if (__builtin_constant_p(len) && len == 24)
+ return siphash_3u64(le64_to_cpu(data[0]), le64_to_cpu(data[1]),
+ le64_to_cpu(data[2]), key);
+ if (__builtin_constant_p(len) && len == 32)
+ return siphash_4u64(le64_to_cpu(data[0]), le64_to_cpu(data[1]),
+ le64_to_cpu(data[2]), le64_to_cpu(data[3]),
+ key);
+ return __siphash_aligned(data, len, key);
+}
+
+/**
+ * siphash - compute 64-bit siphash PRF value
+ * @data: buffer to hash
+ * @size: size of @data
+ * @key: the siphash key
+ */
+static inline u64 siphash(const void *data, size_t len,
+ const siphash_key_t *key)
+{
+ if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) ||
+ !IS_ALIGNED((unsigned long)data, SIPHASH_ALIGNMENT))
+ return __siphash_unaligned(data, len, key);
+ return ___siphash_aligned(data, len, key);
+}
+
+#define HSIPHASH_ALIGNMENT __alignof__(unsigned long)
+typedef struct {
+ unsigned long key[2];
+} hsiphash_key_t;
+
+u32 __hsiphash_aligned(const void *data, size_t len,
+ const hsiphash_key_t *key);
+u32 __hsiphash_unaligned(const void *data, size_t len,
+ const hsiphash_key_t *key);
+
+u32 hsiphash_1u32(const u32 a, const hsiphash_key_t *key);
+u32 hsiphash_2u32(const u32 a, const u32 b, const hsiphash_key_t *key);
+u32 hsiphash_3u32(const u32 a, const u32 b, const u32 c,
+ const hsiphash_key_t *key);
+u32 hsiphash_4u32(const u32 a, const u32 b, const u32 c, const u32 d,
+ const hsiphash_key_t *key);
+
+static inline u32 ___hsiphash_aligned(const __le32 *data, size_t len,
+ const hsiphash_key_t *key)
+{
+ if (__builtin_constant_p(len) && len == 4)
+ return hsiphash_1u32(le32_to_cpu(data[0]), key);
+ if (__builtin_constant_p(len) && len == 8)
+ return hsiphash_2u32(le32_to_cpu(data[0]), le32_to_cpu(data[1]),
+ key);
+ if (__builtin_constant_p(len) && len == 12)
+ return hsiphash_3u32(le32_to_cpu(data[0]), le32_to_cpu(data[1]),
+ le32_to_cpu(data[2]), key);
+ if (__builtin_constant_p(len) && len == 16)
+ return hsiphash_4u32(le32_to_cpu(data[0]), le32_to_cpu(data[1]),
+ le32_to_cpu(data[2]), le32_to_cpu(data[3]),
+ key);
+ return __hsiphash_aligned(data, len, key);
+}
+
+/**
+ * hsiphash - compute 32-bit hsiphash PRF value
+ * @data: buffer to hash
+ * @size: size of @data
+ * @key: the hsiphash key
+ */
+static inline u32 hsiphash(const void *data, size_t len,
+ const hsiphash_key_t *key)
+{
+ if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) ||
+ !IS_ALIGNED((unsigned long)data, HSIPHASH_ALIGNMENT))
+ return __hsiphash_unaligned(data, len, key);
+ return ___hsiphash_aligned(data, len, key);
+}
+
+/*
+ * These macros expose the raw SipHash and HalfSipHash permutations.
+ * Do not use them directly! If you think you have a use for them,
+ * be sure to CC the maintainer of this file explaining why.
+ */
+
+#define SIPHASH_PERMUTATION(a, b, c, d) ( \
+ (a) += (b), (b) = rol64((b), 13), (b) ^= (a), (a) = rol64((a), 32), \
+ (c) += (d), (d) = rol64((d), 16), (d) ^= (c), \
+ (a) += (d), (d) = rol64((d), 21), (d) ^= (a), \
+ (c) += (b), (b) = rol64((b), 17), (b) ^= (c), (c) = rol64((c), 32))
+
+#define SIPHASH_CONST_0 0x736f6d6570736575ULL
+#define SIPHASH_CONST_1 0x646f72616e646f6dULL
+#define SIPHASH_CONST_2 0x6c7967656e657261ULL
+#define SIPHASH_CONST_3 0x7465646279746573ULL
+
+#define HSIPHASH_PERMUTATION(a, b, c, d) ( \
+ (a) += (b), (b) = rol32((b), 5), (b) ^= (a), (a) = rol32((a), 16), \
+ (c) += (d), (d) = rol32((d), 8), (d) ^= (c), \
+ (a) += (d), (d) = rol32((d), 7), (d) ^= (a), \
+ (c) += (b), (b) = rol32((b), 13), (b) ^= (c), (c) = rol32((c), 16))
+
+#define HSIPHASH_CONST_0 0U
+#define HSIPHASH_CONST_1 0U
+#define HSIPHASH_CONST_2 0x6c796765U
+#define HSIPHASH_CONST_3 0x74656462U
+
+#endif /* _LINUX_SIPHASH_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/skbuff.h b/sys/compat/linuxkpi/common/include/linux/skbuff.h
index 2e560a120e41..c43d6daff5ee 100644
--- a/sys/compat/linuxkpi/common/include/linux/skbuff.h
+++ b/sys/compat/linuxkpi/common/include/linux/skbuff.h
@@ -770,7 +770,7 @@ ___skb_queue_splice(const struct sk_buff_head *from,
}
static inline void
-skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to)
+skb_queue_splice(const struct sk_buff_head *from, struct sk_buff_head *to)
{
SKB_TRACE2(from, to);
@@ -780,6 +780,13 @@ skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to)
___skb_queue_splice(from, (struct sk_buff *)to, to->next);
to->qlen += from->qlen;
+}
+
+static inline void
+skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to)
+{
+
+ skb_queue_splice(from, to);
__skb_queue_head_init(from);
}
diff --git a/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h b/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h
new file mode 100644
index 000000000000..ade0b06d839f
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2026 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H
+#define _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H
+
+#include <linux/kernel.h> /* pr_debug */
+
+enum airoha_npu_wlan_get_cmd {
+ __dummy_airoha_npu_wlan_get_cmd,
+};
+enum airoha_npu_wlan_set_cmd {
+ __dummy_airoha_npu_wlan_set_cmd,
+};
+
+struct airoha_npu {
+};
+struct airoha_npu_rx_dma_desc {
+};
+struct airoha_npu_tx_dma_desc {
+};
+
+static __inline int
+airoha_npu_wlan_send_msg(void *npu, int ifindex,
+ enum airoha_npu_wlan_set_cmd cmd, void *val, size_t len, gfp_t gfp)
+{
+ pr_debug("%s: TODO\n", __func__);
+ return (-EOPNOTSUPP);
+}
+
+static __inline int
+airoha_npu_wlan_get_msg(void *npu, int ifindex,
+ enum airoha_npu_wlan_get_cmd cmd, void *val, size_t len, gfp_t gfp)
+{
+ pr_debug("%s: TODO\n", __func__);
+ return (-EOPNOTSUPP);
+}
+
+static __inline void
+airoha_npu_wlan_enable_irq(struct airoha_npu *npu, int q)
+{
+ pr_debug("%s: TODO\n", __func__);
+}
+
+#endif /* _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h b/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h
index 2b9c6ae4911e..64daa8c78c9d 100644
--- a/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h
+++ b/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2022-2025 Bjoern A. Zeeb
+ * Copyright (c) 2022-2026 Bjoern A. Zeeb
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -37,7 +37,13 @@ mtk_wed_device_active(struct mtk_wed_device *dev __unused)
static inline bool
mtk_wed_get_rx_capa(struct mtk_wed_device *dev __unused)
{
+ pr_debug("%s: TODO\n", __func__);
+ return (false);
+}
+static inline bool
+mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev __unused)
+{
pr_debug("%s: TODO\n", __func__);
return (false);
}
@@ -66,6 +72,12 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev __unused)
{
return (false);
}
+
+static inline bool
+mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev __unused)
+{
+ return (false);
+}
#endif /* CONFIG_NET_MEDIATEK_SOC_WED */
#endif /* _LINUXKPI_LINUX_SOC_MEDIATEK_MTK_WED_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/sort.h b/sys/compat/linuxkpi/common/include/linux/sort.h
index e6196d1f41c7..361b37c587c8 100644
--- a/sys/compat/linuxkpi/common/include/linux/sort.h
+++ b/sys/compat/linuxkpi/common/include/linux/sort.h
@@ -34,7 +34,7 @@
#include <sys/libkern.h>
#define sort(base, num, size, cmp, swap) do { \
- BUILD_BUG_ON_ZERO(swap); \
+ BUILD_BUG_ON(swap); \
qsort(base, num, size, cmp); \
} while (0)
diff --git a/sys/compat/linuxkpi/common/include/linux/spinlock.h b/sys/compat/linuxkpi/common/include/linux/spinlock.h
index dc10b0457153..a786cbab5e13 100644
--- a/sys/compat/linuxkpi/common/include/linux/spinlock.h
+++ b/sys/compat/linuxkpi/common/include/linux/spinlock.h
@@ -36,10 +36,12 @@
#include <sys/mutex.h>
#include <sys/kdb.h>
+#include <linux/cleanup.h>
#include <linux/compiler.h>
#include <linux/rwlock.h>
#include <linux/bottom_half.h>
#include <linux/lockdep.h>
+#include <linux/preempt.h>
typedef struct mtx spinlock_t;
@@ -178,4 +180,33 @@ _atomic_dec_and_lock_irqsave(atomic_t *cnt, spinlock_t *lock,
return (0);
}
+/*
+ * struct raw_spinlock
+ */
+
+typedef struct raw_spinlock {
+ struct mtx lock;
+} raw_spinlock_t;
+
+#define raw_spin_lock_init(rlock) \
+ mtx_init(&(rlock)->lock, spin_lock_name("lnxspin_raw"), \
+ NULL, MTX_DEF | MTX_NOWITNESS | MTX_NEW)
+
+#define raw_spin_lock(rl) spin_lock(&(rl)->lock)
+#define raw_spin_trylock(rl) spin_trylock(&(rl)->lock)
+#define raw_spin_unlock(rl) spin_unlock(&(rl)->lock)
+
+#define raw_spin_lock_irqsave(rl, f) spin_lock_irqsave(&(rl)->lock, (f))
+#define raw_spin_trylock_irqsave(rl, f) spin_trylock_irqsave(&(rl)->lock, (f))
+#define raw_spin_unlock_irqrestore(rl, f) spin_unlock_irqrestore(&(rl)->lock, (f))
+
+/*
+ * cleanup.h related pre-defined cases.
+ */
+DEFINE_LOCK_GUARD_1(spinlock_irqsave,
+ spinlock_t,
+ spin_lock_irqsave(_T->lock, _T->flags),
+ spin_unlock_irqrestore(_T->lock, _T->flags),
+ unsigned long flags)
+
#endif /* _LINUXKPI_LINUX_SPINLOCK_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/ssb/ssb_regs.h b/sys/compat/linuxkpi/common/include/linux/ssb/ssb_regs.h
new file mode 100644
index 000000000000..e1c18b6b632a
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/ssb/ssb_regs.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2025 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_SSB_SSB_REGS_H
+#define _LINUXKPI_LINUX_SSB_SSB_REGS_H
+
+#define SSB_IDHIGH_RCHI 0x00007000
+#define SSB_IDHIGH_RCHI_SHIFT 8
+#define SSB_IDHIGH_RCLO 0x0000000F
+#define SSB_IDLOW_INITIATOR 0x00000080
+#define SSB_IMSTATE_BUSY 0x01800000
+#define SSB_IMSTATE_IBE 0x00020000
+#define SSB_IMSTATE_REJECT 0x02000000
+#define SSB_IMSTATE_TO 0x00040000
+#define SSB_TMSHIGH_BUSY 0x00000004
+#define SSB_TMSHIGH_SERR 0x00000001
+#define SSB_TMSLOW_CLOCK 0x00010000
+#define SSB_TMSLOW_FGC 0x00020000
+#define SSB_TMSLOW_REJECT 0x00000002
+#define SSB_TMSLOW_RESET 0x00000001
+
+#endif /* _LINUXKPI_LINUX_SSB_SSB_REGS_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/string_choices.h b/sys/compat/linuxkpi/common/include/linux/string_choices.h
index 74aa3fd019b2..db540d3e7d40 100644
--- a/sys/compat/linuxkpi/common/include/linux/string_choices.h
+++ b/sys/compat/linuxkpi/common/include/linux/string_choices.h
@@ -33,39 +33,33 @@
static inline const char *
str_yes_no(bool value)
{
- if (value)
- return "yes";
- else
- return "no";
+ return (value ? "yes" : "no");
}
static inline const char *
str_on_off(bool value)
{
- if (value)
- return "on";
- else
- return "off";
+ return (value ? "on" : "off");
}
static inline const char *
str_enabled_disabled(bool value)
{
- if (value)
- return "enabled";
- else
- return "disabled";
+ return (value ? "enabled" : "disabled");
}
static inline const char *
str_enable_disable(bool value)
{
- if (value)
- return "enable";
- else
- return "disable";
+ return (value ? "enable" : "disable");
}
#define str_disable_enable(_v) str_enable_disable(!(_v))
+static inline const char *
+str_read_write(bool value)
+{
+ return (value ? "read" : "write");
+}
+
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/suspend.h b/sys/compat/linuxkpi/common/include/linux/suspend.h
index dacecbebdc08..3d5d5d594127 100644
--- a/sys/compat/linuxkpi/common/include/linux/suspend.h
+++ b/sys/compat/linuxkpi/common/include/linux/suspend.h
@@ -3,6 +3,12 @@
#ifndef _LINUXKPI_LINUX_SUSPEND_H_
#define _LINUXKPI_LINUX_SUSPEND_H_
+#include <linux/swap.h>
+#include <linux/notifier.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/mm.h>
+
typedef int suspend_state_t;
extern suspend_state_t pm_suspend_target_state;
diff --git a/sys/compat/linuxkpi/common/include/linux/swap.h b/sys/compat/linuxkpi/common/include/linux/swap.h
index 5828db7ae392..9c1db9677f9e 100644
--- a/sys/compat/linuxkpi/common/include/linux/swap.h
+++ b/sys/compat/linuxkpi/common/include/linux/swap.h
@@ -37,7 +37,14 @@
#include <vm/swap_pager.h>
#include <vm/vm_pageout.h>
+#include <linux/spinlock.h>
+#include <linux/mmzone.h>
+#include <linux/list.h>
+#include <linux/memcontrol.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
#include <linux/pagemap.h>
+#include <linux/atomic.h>
#include <linux/page-flags.h>
static inline long
diff --git a/sys/compat/linuxkpi/common/include/linux/sysfs.h b/sys/compat/linuxkpi/common/include/linux/sysfs.h
index 470c224a9778..7c8c4e2e32b9 100644
--- a/sys/compat/linuxkpi/common/include/linux/sysfs.h
+++ b/sys/compat/linuxkpi/common/include/linux/sysfs.h
@@ -43,13 +43,6 @@ struct sysfs_ops {
size_t);
};
-struct attribute_group {
- const char *name;
- mode_t (*is_visible)(struct kobject *,
- struct attribute *, int);
- struct attribute **attrs;
-};
-
struct bin_attribute {
struct attribute attr;
size_t size;
@@ -59,6 +52,14 @@ struct bin_attribute {
struct bin_attribute *, char *, loff_t, size_t);
};
+struct attribute_group {
+ const char *name;
+ mode_t (*is_visible)(struct kobject *,
+ struct attribute *, int);
+ struct attribute **attrs;
+ struct bin_attribute **bin_attrs;
+};
+
#define __ATTR(_name, _mode, _show, _store) { \
.attr = { .name = __stringify(_name), .mode = _mode }, \
.show = _show, .store = _store, \
@@ -370,6 +371,7 @@ static inline int
sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
{
struct attribute **attr;
+ struct bin_attribute **bin_attr;
struct sysctl_oid *oidp;
/* Don't create the group node if grp->name is undefined. */
@@ -378,11 +380,19 @@ sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name);
else
oidp = kobj->oidp;
- for (attr = grp->attrs; *attr != NULL; attr++) {
+ for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) {
SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO,
(*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE,
kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", "");
}
+ for (bin_attr = grp->bin_attrs;
+ bin_attr != NULL && *bin_attr != NULL;
+ bin_attr++) {
+ SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO,
+ (*bin_attr)->attr.name,
+ CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_MPSAFE,
+ kobj, (uintptr_t)*bin_attr, sysctl_handle_bin_attr, "", "");
+ }
return (0);
}
@@ -434,14 +444,20 @@ static inline void
sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp)
{
struct attribute **attr;
+ struct bin_attribute **bin_attr;
struct sysctl_oid *oidp;
SYSCTL_FOREACH(oidp, SYSCTL_CHILDREN(kobj->oidp)) {
if (strcmp(oidp->oid_name, grp->name) != 0)
continue;
- for (attr = grp->attrs; *attr != NULL; attr++) {
+ for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) {
sysctl_remove_name(oidp, (*attr)->name, 1, 1);
}
+ for (bin_attr = grp->bin_attrs;
+ bin_attr != NULL && *bin_attr != NULL;
+ bin_attr++) {
+ sysctl_remove_name(oidp, (*bin_attr)->attr.name, 1, 1);
+ }
}
}
diff --git a/sys/compat/linuxkpi/common/include/media/cec-notifier.h b/sys/compat/linuxkpi/common/include/media/cec-notifier.h
new file mode 100644
index 000000000000..8d6fc452b907
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/media/cec-notifier.h
@@ -0,0 +1,17 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ */
+
+#ifndef _LINUXKPI_MEDIA_CEC_NOTIFIER_H_
+#define _LINUXKPI_MEDIA_CEC_NOTIFIER_H_
+
+#include <linux/err.h>
+#include <media/cec.h>
+
+#endif
diff --git a/sys/compat/linuxkpi/common/include/media/cec.h b/sys/compat/linuxkpi/common/include/media/cec.h
new file mode 100644
index 000000000000..c96b57868e23
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/media/cec.h
@@ -0,0 +1,23 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ */
+
+#ifndef _LINUXKPI_MEDIA_CEC_H_
+#define _LINUXKPI_MEDIA_CEC_H_
+
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+#include <linux/cec-funcs.h>
+
+#endif
diff --git a/sys/compat/linuxkpi/common/include/net/cfg80211.h b/sys/compat/linuxkpi/common/include/net/cfg80211.h
index d7ed2bc97c98..5c8c914bdee7 100644
--- a/sys/compat/linuxkpi/common/include/net/cfg80211.h
+++ b/sys/compat/linuxkpi/common/include/net/cfg80211.h
@@ -124,6 +124,7 @@ enum ieee80211_channel_flags {
IEEE80211_CHAN_PSD = BIT(12),
IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP = BIT(13),
IEEE80211_CHAN_CAN_MONITOR = BIT(14),
+ IEEE80211_CHAN_NO_EHT = BIT(15),
};
#define IEEE80211_CHAN_NO_HT40 (IEEE80211_CHAN_NO_HT40MINUS|IEEE80211_CHAN_NO_HT40PLUS)
@@ -152,6 +153,8 @@ struct linuxkpi_ieee80211_channel {
int orig_mpwr;
};
+#define NL80211_EHT_NSS_MAX 16
+
struct cfg80211_bitrate_mask {
/* TODO FIXME */
struct {
@@ -159,6 +162,7 @@ struct cfg80211_bitrate_mask {
uint8_t ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
uint16_t vht_mcs[8];
uint16_t he_mcs[8];
+ uint16_t eht_mcs[NL80211_EHT_NSS_MAX];
enum nl80211_txrate_gi gi;
enum nl80211_he_gi he_gi;
uint8_t he_ltf; /* XXX enum? */
@@ -1017,6 +1021,14 @@ struct survey_info { /* net80211::struct ieee80211_channel_survey */
struct linuxkpi_ieee80211_channel *channel;
};
+enum wiphy_bss_param_flags {
+ WIPHY_BSS_PARAM_AP_ISOLATE = BIT(0),
+};
+
+struct bss_parameters {
+ int ap_isolate;
+};
+
enum wiphy_vendor_cmd_need_flags {
WIPHY_VENDOR_CMD_NEED_NETDEV = 0x01,
WIPHY_VENDOR_CMD_NEED_RUNNING = 0x02,
@@ -1138,6 +1150,8 @@ struct wiphy {
int n_radio;
const struct wiphy_radio *radio;
+ uint32_t bss_param_support; /* enum wiphy_bss_param_flags */
+
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;
@@ -1220,7 +1234,7 @@ struct cfg80211_ops {
int (*dump_survey)(struct wiphy *, struct net_device *, int, struct survey_info *);
int (*external_auth)(struct wiphy *, struct net_device *, struct cfg80211_external_auth_params *);
int (*set_cqm_rssi_range_config)(struct wiphy *, struct net_device *, int, int);
-
+ int (*change_bss)(struct wiphy *, struct net_device *, struct bss_parameters *);
};
@@ -1230,6 +1244,7 @@ struct cfg80211_ops {
struct wiphy *linuxkpi_wiphy_new(const struct cfg80211_ops *, size_t);
void linuxkpi_wiphy_free(struct wiphy *wiphy);
+int linuxkpi_80211_wiphy_register(struct wiphy *);
void linuxkpi_wiphy_work_queue(struct wiphy *, struct wiphy_work *);
void linuxkpi_wiphy_work_cancel(struct wiphy *, struct wiphy_work *);
@@ -1749,8 +1764,7 @@ wiphy_net(struct wiphy *wiphy)
static __inline int
wiphy_register(struct wiphy *wiphy)
{
- TODO();
- return (0);
+ return (linuxkpi_80211_wiphy_register(wiphy));
}
static __inline void
diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h
index 6e2f3f2d8781..4f3aad532810 100644
--- a/sys/compat/linuxkpi/common/include/net/mac80211.h
+++ b/sys/compat/linuxkpi/common/include/net/mac80211.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
* Copyright (c) 2020-2025 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -431,7 +431,6 @@ enum ieee80211_hw_flags {
IEEE80211_HW_BUFF_MMPDU_TXQ,
IEEE80211_HW_CHANCTX_STA_CSA,
IEEE80211_HW_CONNECTION_MONITOR,
- IEEE80211_HW_DEAUTH_NEED_MGD_TX_PREP,
IEEE80211_HW_HAS_RATE_CONTROL,
IEEE80211_HW_MFP_CAPABLE,
IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
@@ -789,10 +788,21 @@ struct ieee80211_tx_queue_params {
struct ieee80211_he_mu_edca_param_ac_rec mu_edca_param_rec;
};
+enum mac80211_rate_control_flags {
+ IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(0),
+ IEEE80211_TX_RC_80_MHZ_WIDTH = BIT(1),
+ IEEE80211_TX_RC_160_MHZ_WIDTH = BIT(2),
+ IEEE80211_TX_RC_GREEN_FIELD = BIT(3),
+ IEEE80211_TX_RC_MCS = BIT(4),
+ IEEE80211_TX_RC_SHORT_GI = BIT(5),
+ IEEE80211_TX_RC_VHT_MCS = BIT(6),
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(7),
+};
+
struct ieee80211_tx_rate {
uint8_t idx;
uint16_t count:5,
- flags:11;
+ flags:11; /* enum mac80211_rate_control_flags */
};
enum ieee80211_vif_driver_flags {
@@ -1092,6 +1102,8 @@ struct ieee80211_ops {
void (*rfkill_poll)(struct ieee80211_hw *);
+ int (*net_fill_forward_path)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct net_device_path_ctx *, struct net_device_path *);
+
/* #ifdef CONFIG_MAC80211_DEBUGFS */ /* Do not change depending on compile-time option. */
void (*sta_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct dentry *);
void (*vif_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *);
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index 02724433d89d..01347586ef63 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
* Copyright (c) 2020-2025 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -282,7 +282,7 @@ lkpi_nl80211_sta_info_to_str(struct sbuf *s, const char *prefix,
}
static void
-lkpi_80211_dump_lvif_stas(struct lkpi_vif *lvif, struct sbuf *s)
+lkpi_80211_dump_lvif_stas(struct lkpi_vif *lvif, struct sbuf *s, bool dump_queues)
{
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
@@ -292,6 +292,7 @@ lkpi_80211_dump_lvif_stas(struct lkpi_vif *lvif, struct sbuf *s)
struct ieee80211_sta *sta;
struct station_info sinfo;
int error;
+ uint8_t tid;
vif = LVIF_TO_VIF(lvif);
vap = LVIF_TO_VAP(lvif);
@@ -376,6 +377,39 @@ lkpi_80211_dump_lvif_stas(struct lkpi_vif *lvif, struct sbuf *s)
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);
+
+ if (!dump_queues)
+ continue;
+
+ /* Dump queue information. */
+ sbuf_printf(s, " Queue information:\n");
+ sbuf_printf(s, " frms direct tx %ju\n", lsta->frms_tx);
+ for (tid = 0; tid <= IEEE80211_NUM_TIDS; tid++) {
+ struct lkpi_txq *ltxq;
+
+ if (sta->txq[tid] == NULL) {
+ sbuf_printf(s, " tid %-2u NOQ\n", tid);
+ continue;
+ }
+
+ ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
+#ifdef __notyet__
+ sbuf_printf(s, " tid %-2u flags: %b "
+ "txq_generation %u skbq len %d\n",
+ tid, ltxq->flags, LKPI_TXQ_FLAGS_BITS,
+ ltxq->txq_generation,
+ skb_queue_len_lockless(&ltxq->skbq));
+#else
+ sbuf_printf(s, " tid %-2u "
+ "txq_generation %u skbq len %d\n",
+ tid,
+ ltxq->txq_generation,
+ skb_queue_len_lockless(&ltxq->skbq));
+#endif
+ sbuf_printf(s, " frms_enqueued %ju frms_dequeued %ju "
+ "frms_tx %ju\n",
+ ltxq->frms_enqueued, ltxq->frms_dequeued, ltxq->frms_tx);
+ }
}
wiphy_unlock(hw->wiphy);
}
@@ -393,7 +427,28 @@ lkpi_80211_dump_stas(SYSCTL_HANDLER_ARGS)
sbuf_new_for_sysctl(&s, NULL, 1024, req);
- lkpi_80211_dump_lvif_stas(lvif, &s);
+ lkpi_80211_dump_lvif_stas(lvif, &s, false);
+
+ sbuf_finish(&s);
+ sbuf_delete(&s);
+
+ return (0);
+}
+
+static int
+lkpi_80211_dump_sta_queues(SYSCTL_HANDLER_ARGS)
+{
+ struct lkpi_vif *lvif;
+ struct sbuf s;
+
+ if (req->newptr)
+ return (EPERM);
+
+ lvif = (struct lkpi_vif *)arg1;
+
+ sbuf_new_for_sysctl(&s, NULL, 1024, req);
+
+ lkpi_80211_dump_lvif_stas(lvif, &s, true);
sbuf_finish(&s);
sbuf_delete(&s);
@@ -638,7 +693,8 @@ skip_bw:
sta = IEEE80211_VHT_MCS_NOT_SUPPORTED;
else {
sta = MIN(sta, card);
- rx_nss = i + 1;
+ if (rx_nss == 0)
+ rx_nss = i + 1;
}
}
rx_map |= (sta << (2 * i));
@@ -840,41 +896,34 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
/* Deflink information. */
for (band = 0; band < NUM_NL80211_BANDS; band++) {
struct ieee80211_supported_band *supband;
+ uint32_t rate_mandatory;;
supband = hw->wiphy->bands[band];
if (supband == NULL)
continue;
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ /* We have to assume 11g support here. */
+ rate_mandatory = IEEE80211_RATE_MANDATORY_G |
+ IEEE80211_RATE_MANDATORY_B;
+ break;
+ case NL80211_BAND_5GHZ:
+ rate_mandatory = IEEE80211_RATE_MANDATORY_A;
+ break;
+ default:
+ continue;
+ }
+
for (i = 0; i < supband->n_bitrates; i++) {
- switch (band) {
- case NL80211_BAND_2GHZ:
- switch (supband->bitrates[i].bitrate) {
- case 240: /* 11g only */
- case 120: /* 11g only */
- case 110:
- case 60: /* 11g only */
- case 55:
- case 20:
- case 10:
- sta->deflink.supp_rates[band] |= BIT(i);
- break;
- }
- break;
- case NL80211_BAND_5GHZ:
- switch (supband->bitrates[i].bitrate) {
- case 240:
- case 120:
- case 60:
- sta->deflink.supp_rates[band] |= BIT(i);
- break;
- }
- break;
- }
+ if ((supband->bitrates[i].flags & rate_mandatory) != 0)
+ sta->deflink.supp_rates[band] |= BIT(i);
}
}
sta->deflink.smps_mode = IEEE80211_SMPS_OFF;
sta->deflink.bandwidth = IEEE80211_STA_RX_BW_20;
+ sta->deflink.agg.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;
sta->deflink.rx_nss = 1;
sta->deflink.sta = sta;
@@ -1324,6 +1373,15 @@ lkpi_sta_del_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return (0);
lockdep_assert_wiphy(hw->wiphy);
+
+ if (vif->cfg.assoc && lsta->state == IEEE80211_STA_AUTHORIZED) {
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(lsta->ni->ni_ic,
+ "%d %lu %s: vif still assoc; not deleting keys\n",
+ curthread->td_tid, jiffies, __func__);
+ return (0);
+ }
+
ieee80211_ref_node(lsta->ni);
error = 0;
@@ -1403,6 +1461,15 @@ lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
*/
lockdep_assert_wiphy(hw->wiphy);
+ 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);
+ }
+
/*
* While we are assoc we may still send packets. We cannot delete the
* keys as otherwise packets could go out unencrypted. Some firmware
@@ -1413,30 +1480,24 @@ lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
* How to test: run 800Mbit/s UDP traffic and during that restart your
* supplicant. You want to survive that.
*/
- if (vif->cfg.assoc) {
+ if (vif->cfg.assoc && lsta->state == IEEE80211_STA_AUTHORIZED) {
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
ic_printf(ic, "%d %lu %s: vif still assoc; not deleting keys\n",
curthread->td_tid, jiffies, __func__);
+ ieee80211_free_node(ni);
return (0);
}
if (IEEE80211_KEY_UNDEFINED(k)) {
ic_printf(ic, "%s: vap %p key %p is undefined: %p %u\n",
__func__, vap, k, k->wk_cipher, k->wk_keyix);
+ ieee80211_free_node(ni);
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);
}
@@ -1943,7 +2004,7 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni,
* we set the BSS_CHANGED_BEACON_INFO on the non-teardown
* path so make sure we only do run this check once we are
* assoc. (*iv_recv_mgmt)() will be called before we enter
- * here so the ni will be updates with information from the
+ * here so the ni will be updated with information from the
* beacon via net80211::sta_recv_mgmt(). We also need to
* make sure we do not do it on every beacon we still may
* get so only do if something changed. vif->bss_conf.dtim_period
@@ -2060,7 +2121,7 @@ lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
* 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.
+ * See DOWN4 for more detailed comment.
*/
lvif = VIF_TO_LVIF(vif);
@@ -2101,7 +2162,7 @@ lkpi_wake_tx_queues(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
if (no_emptyq && ltxq_empty)
continue;
- lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
+ lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid], false);
}
}
@@ -2181,19 +2242,70 @@ lkpi_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
free(lchanctx, M_LKPI80211);
}
+/* -------------------------------------------------------------------------- */
+
+/* Any other options belong here? Check more drivers. */
+#define BSS_CHANGED_VIF_CFG_BITS \
+ (BSS_CHANGED_SSID | BSS_CHANGED_IDLE | BSS_CHANGED_PS | BSS_CHANGED_ASSOC | \
+ BSS_CHANGED_ARP_FILTER | BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_TTLM)
+
+static void
+lkpi_bss_info_change(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ enum ieee80211_bss_changed bss_changed)
+{
+ struct lkpi_vif *lvif;
+ enum ieee80211_bss_changed vif_cfg_bits, link_info_bits;
+
+ if (ieee80211_vif_is_mld(vif)) {
+ TODO("This likely needs a subset only; split up into 3 parts.");
+ }
+
+ /* Nothing to do? */
+ if (bss_changed == 0)
+ return;
+
+ /*
+ * If the vif is not known to the driver there is nothing to notifiy for.
+ * We MUST NOT check for !lvif_bss_synched here (the reasonable it seems)
+ * as we need to execute the update(s) or we will have follow-up issues.
+ */
+ lvif = VIF_TO_LVIF(vif);
+ if (!lvif->added_to_drv)
+ return;
+
+ /*
+ * With the advent of MLO bss_conf got split up into vif and link
+ * change notfications, while historically it was one.
+ * We now need to support all possible models.
+ */
+ vif_cfg_bits = bss_changed & BSS_CHANGED_VIF_CFG_BITS;
+ if (vif_cfg_bits != 0)
+ lkpi_80211_mo_vif_cfg_changed(hw, vif, vif_cfg_bits, false);
+
+ link_info_bits = bss_changed & ~(BSS_CHANGED_VIF_CFG_BITS);
+ if (link_info_bits != 0)
+ lkpi_80211_mo_link_info_changed(hw, vif, &vif->bss_conf,
+ link_info_bits, 0, false);
+
+ lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+}
/* -------------------------------------------------------------------------- */
static int
lkpi_sta_state_do_nada(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
-
return (0);
}
-/* lkpi_iv_newstate() handles the stop scan case generally. */
-#define lkpi_sta_scan_to_init(_v, _n, _a) lkpi_sta_state_do_nada(_v, _n, _a)
+/* UP1 */
+static int
+lkpi_sta_init_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ return (lkpi_sta_state_do_nada(vap, nstate, arg));
+}
+/* UP2 */
static int
lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
@@ -2392,7 +2504,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* RATES */
IMPROVE("bss info: not all needs to come now and rates are missing");
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ lkpi_bss_info_change(hw, vif, bss_changed);
/*
* Given ni and lsta are 1:1 from alloc to free we can assert that
@@ -2437,7 +2549,9 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* Start mgd_prepare_tx. */
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.duration = PREP_TX_INFO_DURATION;
+ prep_tx_info.duration = PREP_TX_INFO_DURATION; /* SAE */
+ prep_tx_info.subtype = IEEE80211_STYPE_AUTH;
+ prep_tx_info.link_id = 0;
lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
lsta->in_mgd = true;
@@ -2518,136 +2632,7 @@ out_relocked:
return (error);
}
-static int
-lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- struct lkpi_hw *lhw;
- struct ieee80211_hw *hw;
- struct lkpi_vif *lvif;
- struct ieee80211_vif *vif;
- struct ieee80211_node *ni;
- struct lkpi_sta *lsta;
- struct ieee80211_sta *sta;
- struct ieee80211_prep_tx_info prep_tx_info;
- enum ieee80211_bss_changed bss_changed;
- int error;
-
- lhw = vap->iv_ic->ic_softc;
- hw = LHW_TO_HW(lhw);
- lvif = VAP_TO_LVIF(vap);
- vif = LVIF_TO_VIF(lvif);
-
- LKPI_80211_LVIF_LOCK(lvif);
-#ifdef LINUXKPI_DEBUG_80211
- /* XXX-BZ KASSERT later; state going down so no action. */
- if (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\n", __func__, __LINE__,
- lvif, vap, vap->iv_bss, lvif->lvif_bss,
- (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
- lvif->lvif_bss_synched);
-#endif
-
- lsta = lvif->lvif_bss;
- LKPI_80211_LVIF_UNLOCK(lvif);
- KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
- "lvif %p vap %p\n", __func__,
- lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
- ni = lsta->ni; /* Reference held for lvif_bss. */
- sta = LSTA_TO_STA(lsta);
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
- IEEE80211_UNLOCK(vap->iv_ic);
- 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, false, true);
-
- /* flush, no drop */
- lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false);
-
- /* End mgd_complete_tx. */
- if (lsta->in_mgd) {
- memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.success = false;
- lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
- lsta->in_mgd = false;
- }
-
- /* sync_rx_queues */
- lkpi_80211_mo_sync_rx_queues(hw);
-
- /* sta_pre_rcu_remove */
- lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
-
- /* Take the station down. */
-
- /* Adjust sta and change state (from NONE) to NOTEXIST. */
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
- KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
- "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
- if (error != 0) {
- IMPROVE("do we need to undo the chan ctx?");
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
-#if 0
- lsta->added_to_drv = false; /* mo manages. */
-#endif
-
- bss_changed = 0;
- vif->bss_conf.dtim_period = 0; /* go back to 0. */
- bss_changed |= BSS_CHANGED_BEACON_INFO;
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
- LKPI_80211_LVIF_LOCK(lvif);
- /* Remove ni reference for this cache of lsta. */
- lvif->lvif_bss = NULL;
- lvif->lvif_bss_synched = false;
- LKPI_80211_LVIF_UNLOCK(lvif);
- lkpi_lsta_remove(lsta, lvif);
-
- /* conf_tx */
-
- lkpi_remove_chanctx(hw, vif);
-
-out:
- wiphy_unlock(hw->wiphy);
- IEEE80211_LOCK(vap->iv_ic);
- if (error == 0) {
- /*
- * We do this outside the wiphy lock as net80211::node_free() may call
- * into crypto code to delete keys and we have a recursed on
- * non-recursive sx panic. Also only do this if we get here w/o error.
- *
- * 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
- * and potentially freed.
- */
- ieee80211_free_node(ni);
- }
- return (error);
-}
-
-static int
-lkpi_sta_auth_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- int error;
-
- error = lkpi_sta_auth_to_scan(vap, nstate, arg);
- if (error == 0)
- error = lkpi_sta_scan_to_init(vap, nstate, arg);
- return (error);
-}
-
+/* UP3.1 */
static int
lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
@@ -2702,23 +2687,28 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in
/* End mgd_complete_tx. */
if (lsta->in_mgd) {
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+ prep_tx_info.subtype = IEEE80211_STYPE_AUTH;
prep_tx_info.success = true;
lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
lsta->in_mgd = false;
}
- /* Now start assoc. */
+ /* Now start assoc. unless nstate=RUN (auth_to_run). */
/* Start mgd_prepare_tx. */
- if (!lsta->in_mgd) {
+ if (nstate == IEEE80211_S_ASSOC && !lsta->in_mgd) {
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.duration = PREP_TX_INFO_DURATION;
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ prep_tx_info.link_id = 0;
lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
lsta->in_mgd = true;
}
+#if 0
+ /* We do not yet have a packet to go out. */
/* Wake tx queue to get packet out. */
lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), false, true);
+#endif
/*
* <twiddle> .. we end up in "assoc_to_run"
@@ -2736,286 +2726,21 @@ out:
return (error);
}
-/* auth_to_auth, assoc_to_assoc. */
-static int
-lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- struct lkpi_hw *lhw;
- struct ieee80211_hw *hw;
- struct lkpi_vif *lvif;
- struct ieee80211_vif *vif;
- struct lkpi_sta *lsta;
- struct ieee80211_prep_tx_info prep_tx_info;
- int error;
-
- lhw = vap->iv_ic->ic_softc;
- hw = LHW_TO_HW(lhw);
- lvif = VAP_TO_LVIF(vap);
- vif = LVIF_TO_VIF(lvif);
-
- IEEE80211_UNLOCK(vap->iv_ic);
- wiphy_lock(hw->wiphy);
-
- LKPI_80211_LVIF_LOCK(lvif);
- /* XXX-BZ KASSERT later? */
- if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
-#ifdef LINUXKPI_DEBUG_80211
- ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
- "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
- lvif, vap, vap->iv_bss, lvif->lvif_bss,
- (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
- lvif->lvif_bss_synched);
-#endif
- LKPI_80211_LVIF_UNLOCK(lvif);
- error = ENOTRECOVERABLE;
- goto out;
- }
- lsta = lvif->lvif_bss;
- LKPI_80211_LVIF_UNLOCK(lvif);
-
- KASSERT(lsta != NULL, ("%s: lsta %p! lvif %p vap %p\n", __func__,
- lsta, lvif, vap));
-
- IMPROVE("event callback?");
-
- /* End mgd_complete_tx. */
- if (lsta->in_mgd) {
- memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.success = false;
- lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
- lsta->in_mgd = false;
- }
-
- /* Now start assoc. */
-
- /* Start mgd_prepare_tx. */
- if (!lsta->in_mgd) {
- memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.duration = PREP_TX_INFO_DURATION;
- lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
- lsta->in_mgd = true;
- }
-
- error = 0;
-out:
- wiphy_unlock(hw->wiphy);
- IEEE80211_LOCK(vap->iv_ic);
-
- return (error);
-}
-
-static int
-_lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- struct lkpi_hw *lhw;
- struct ieee80211_hw *hw;
- struct lkpi_vif *lvif;
- struct ieee80211_vif *vif;
- struct ieee80211_node *ni;
- struct lkpi_sta *lsta;
- struct ieee80211_sta *sta;
- struct ieee80211_prep_tx_info prep_tx_info;
- enum ieee80211_bss_changed bss_changed;
- int error;
-
- lhw = vap->iv_ic->ic_softc;
- hw = LHW_TO_HW(lhw);
- lvif = VAP_TO_LVIF(vap);
- vif = LVIF_TO_VIF(lvif);
-
- IEEE80211_UNLOCK(vap->iv_ic);
- wiphy_lock(hw->wiphy);
-
- LKPI_80211_LVIF_LOCK(lvif);
-#ifdef LINUXKPI_DEBUG_80211
- /* XXX-BZ KASSERT later; state going down so no action. */
- if (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\n", __func__, __LINE__,
- lvif, vap, vap->iv_bss, lvif->lvif_bss,
- (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
- lvif->lvif_bss_synched);
-#endif
- lsta = lvif->lvif_bss;
- LKPI_80211_LVIF_UNLOCK(lvif);
- KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
- "lvif %p vap %p\n", __func__,
- lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
-
- ni = lsta->ni; /* Reference held for lvif_bss. */
- sta = LSTA_TO_STA(lsta);
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
- /* flush, drop. */
- lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
-
- IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
- if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
- !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;
- }
-
- wiphy_unlock(hw->wiphy);
- IEEE80211_LOCK(vap->iv_ic);
-
- /* 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) "
- "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
- goto outni;
- }
-
- IEEE80211_UNLOCK(vap->iv_ic);
-
- /* 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, false, true);
-
- /* flush, no drop */
- lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false);
-
- /* End mgd_complete_tx. */
- 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;
- }
-
- /* sync_rx_queues */
- lkpi_80211_mo_sync_rx_queues(hw);
-
- /* sta_pre_rcu_remove */
- lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
-
- /* Take the station down. */
-
- /* Update sta and change state (from AUTH) to NONE. */
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
- KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
- "AUTH: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
- if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
-
- /* See comment in lkpi_sta_run_to_init(). */
- bss_changed = 0;
- bss_changed |= lkpi_disassoc(sta, vif, lhw);
-
-#ifdef LKPI_80211_HW_CRYPTO
- /*
- * In theory we remove keys here but there must not exist any for this
- * state change until we clean them up again into small steps and no
- * code duplication.
- */
-#endif
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
- /* Adjust sta and change state (from NONE) to NOTEXIST. */
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
- KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
- "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
- if (error != 0) {
- IMPROVE("do we need to undo the chan ctx?");
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* sta no longer save to use. */
-
- IMPROVE("Any bss_info changes to announce?");
- 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.dtim_period = 0; /* go back to 0. */
- bss_changed |= BSS_CHANGED_BEACON_INFO;
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
-
- LKPI_80211_LVIF_LOCK(lvif);
- /* Remove ni reference for this cache of lsta. */
- lvif->lvif_bss = NULL;
- lvif->lvif_bss_synched = false;
- LKPI_80211_LVIF_UNLOCK(lvif);
- lkpi_lsta_remove(lsta, lvif);
-
- /* conf_tx */
-
- lkpi_remove_chanctx(hw, vif);
-
- error = EALREADY;
-out:
- wiphy_unlock(hw->wiphy);
- IEEE80211_LOCK(vap->iv_ic);
- if (error == EALREADY) {
- /*
- * We do this outside the wiphy lock as net80211::node_free() may call
- * into crypto code to delete keys and we have a recursed on
- * non-recursive sx panic. Also only do this if we get here w/o error.
- *
- * 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
- * and potentially freed.
- */
- ieee80211_free_node(ni);
- }
-outni:
- return (error);
-}
-
-static int
-lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- int error;
-
- error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
- if (error != 0 && error != EALREADY)
- return (error);
-
- /* At this point iv_bss is long a new node! */
-
- error |= lkpi_sta_scan_to_auth(vap, nstate, 0);
- return (error);
-}
-
-static int
-lkpi_sta_assoc_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- int error;
-
- error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
- return (error);
-}
+static int lkpi_sta_assoc_to_run(struct ieee80211vap *, enum ieee80211_state, int);
+/* UP3.2 */
static int
-lkpi_sta_assoc_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+lkpi_sta_auth_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
int error;
- error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
+ error = lkpi_sta_auth_to_assoc(vap, nstate, arg);
+ if (error == 0)
+ error = lkpi_sta_assoc_to_run(vap, nstate, arg);
return (error);
}
+/* UP4 */
static int
lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
@@ -3063,7 +2788,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
IMPROVE("ponder some of this moved to ic_newassoc, scan_assoc_success, "
"and to lesser extend ieee80211_notify_node_join");
- /* Finish assoc. */
+ /* Finish assoc. (even if this is auth_to_run!) */
/* Update sta_state (AUTH to ASSOC) and set aid. */
KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
"AUTH: %#x\n", __func__, lsta, lsta->state));
@@ -3113,16 +2838,18 @@ 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);
+ lkpi_bss_info_change(hw, vif, bss_changed);
/* - change_chanctx (if needed)
* - event_callback
*/
- /* End mgd_complete_tx. */
+ /* End mgd_complete_tx. (we do not have to check ostate == IEEE80211_S_ASSOC). */
if (lsta->in_mgd) {
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.success = true;
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ prep_tx_info.success = true; /* Needs vif->cfg.assoc set! */
+ prep_tx_info.link_id = 0;
lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
lsta->in_mgd = false;
}
@@ -3171,7 +2898,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
bss_changed = 0;
bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ lkpi_bss_info_change(hw, vif, bss_changed);
/* Prepare_multicast && configure_filter. */
lkpi_update_mcast_filter(vap->iv_ic);
@@ -3182,17 +2909,14 @@ out:
return (error);
}
-static int
-lkpi_sta_auth_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- int error;
-
- error = lkpi_sta_auth_to_assoc(vap, nstate, arg);
- if (error == 0)
- error = lkpi_sta_assoc_to_run(vap, nstate, arg);
- return (error);
-}
-
+/*
+ * DOWN1
+ * "to assoc" means we are going back to State 2 from State 4[/3].
+ * This means ni still is authenticated, so we keep sta, chanctx, ..
+ * We will send a (Re)Assoc Request in case net80211 handles roadming.
+ * Note: this can be called as part of a DEAUTH going to State 1 as well,
+ * so for RoC prep_tx_info we need to check nstate (see run_to_{auth,scan,init}).
+ */
static int
lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
@@ -3207,6 +2931,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
#if 0
enum ieee80211_bss_changed bss_changed;
#endif
+ struct ieee80211_rx_ampdu *rap;
int error;
lhw = vap->iv_ic->ic_softc;
@@ -3214,6 +2939,9 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
+ IEEE80211_UNLOCK(vap->iv_ic);
+ wiphy_lock(hw->wiphy);
+
LKPI_80211_LVIF_LOCK(lvif);
#ifdef LINUXKPI_DEBUG_80211
/* XXX-BZ KASSERT later; state going down so no action. */
@@ -3235,26 +2963,41 @@ 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);
- wiphy_lock(hw->wiphy);
-
/* flush, drop. */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
- IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
- if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
- !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;
+ /* We should make this a KASSERT. */
+ if (lsta->in_mgd) {
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p lsta %p in_mgd\n",
+ __func__, __LINE__, lvif, vap, lsta);
}
+ /*
+ * Problem is that we should hook into the tx/rx flow and not
+ * try to re-model the state machine parts. We may miss a SME
+ * triggered frame this way.
+ */
+ memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+ if (nstate == IEEE80211_S_ASSOC) {
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
+ if (arg)
+ prep_tx_info.subtype = IEEE80211_STYPE_REASSOC_REQ;
+ else
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ } else {
+ /* wpa_supplicant upon RTM_IEEE80211_LEAVE. */
+ prep_tx_info.subtype = IEEE80211_STYPE_DISASSOC;
+ }
+ } else
+ prep_tx_info.subtype = IEEE80211_STYPE_DEAUTH;
+ prep_tx_info.was_assoc = true;
+ prep_tx_info.link_id = 0;
+ lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
+ lsta->in_mgd = true;
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 (RE-)ASSOC/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) "
@@ -3262,6 +3005,16 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
goto outni;
}
+ /* Stop any BA sessions if still active. */
+ for (int rapn = 0; rapn < WME_NUM_TID; rapn++) {
+ rap = &ni->ni_rx_ampdu[rapn];
+
+ if ((rap->rxa_flags & IEEE80211_AGGR_RUNNING) == 0)
+ continue;
+
+ vap->iv_ic->ic_ampdu_rx_stop(ni, rap);
+ }
+
IEEE80211_UNLOCK(vap->iv_ic);
/* Ensure the packets get out. */
@@ -3278,20 +3031,20 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false);
/* End mgd_complete_tx. */
- 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;
+ /* We should make this a KASSERT. */
+ if (!lsta->in_mgd) {
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p lsta %p !in_mgd\n",
+ __func__, __LINE__, lvif, vap, lsta);
}
+ lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
+ lsta->in_mgd = false;
#if 0
/* sync_rx_queues */
lkpi_80211_mo_sync_rx_queues(hw);
/* sta_pre_rcu_remove */
- lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
+ lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
#endif
/* Take the station down. */
@@ -3340,6 +3093,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
#if 0
/* Update bss info (bss_info_changed) (assoc, aid, ..). */
+ /* See comment in DOWN4. */
lkpi_disassoc(sta, vif, lhw);
#endif
@@ -3351,8 +3105,15 @@ outni:
return (error);
}
+/*
+ * DOWN2
+ * We are in state 2 and go back to state 1 and will try to auth again
+ * (to IEEE80211_S_AUTH in FreeBSD means "try to auth"). This should be
+ * like scan_to_auth but that we keep the "ni" and with that chanctx/bssid,
+ * which essentially makes this "a_to_a" in LinuxKPI.
+ */
static int
-lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
@@ -3360,9 +3121,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
struct ieee80211_vif *vif;
struct ieee80211_node *ni;
struct lkpi_sta *lsta;
- struct ieee80211_sta *sta;
struct ieee80211_prep_tx_info prep_tx_info;
- enum ieee80211_bss_changed bss_changed;
int error;
lhw = vap->iv_ic->ic_softc;
@@ -3390,43 +3149,107 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
ni = lsta->ni; /* Reference held for lvif_bss. */
- sta = LSTA_TO_STA(lsta);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
- /* flush, drop. */
- lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
-
- IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
- if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
- !lsta->in_mgd) {
+ /* End mgd_complete_tx. */
+ if (lsta->in_mgd && vap->iv_state == IEEE80211_S_ASSOC) {
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;
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ prep_tx_info.link_id = 0;
+ lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
+ lsta->in_mgd = false;
+ } else if (lsta->in_mgd) {
+ ic_printf(vap->iv_ic, "%s:%d: in_mgd %d (%s) -> %d (%s) %d\n",
+ __func__, __LINE__,
+ vap->iv_state, ieee80211_state_name[vap->iv_state],
+ nstate, ieee80211_state_name[nstate], arg);
}
- wiphy_unlock(hw->wiphy);
- IEEE80211_LOCK(vap->iv_ic);
-
- /* Call iv_newstate first so we get potential DISASSOC packet out. */
- error = lvif->iv_newstate(vap, nstate, arg);
+ /* Take the station down. */
+ /* Update sta_state (AUTH to NONE). */
+ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
+ KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
+ "AUTH: %#x\n", __func__, lsta, lsta->state));
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
- "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
- goto outni;
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
+ "failed: %d\n", __func__, __LINE__, error);
+ goto out;
}
- IEEE80211_UNLOCK(vap->iv_ic);
+ lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
- /* Ensure the packets get out. */
- lkpi_80211_flush_tx(lhw, lsta);
+out:
+ wiphy_unlock(hw->wiphy);
+ IEEE80211_LOCK(vap->iv_ic);
+ return (error);
+}
+
+/*
+ * DOWN3
+ * We are in state 1. Either auth timed out (arg != 0) or we have an internal
+ * state change forcing us to give up trying to authenticate.
+ * Cleanup and remove chanctx, sta, ...
+ */
+static int
+lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ struct ieee80211_node *ni;
+ struct lkpi_sta *lsta;
+ struct ieee80211_sta *sta;
+ struct ieee80211_prep_tx_info prep_tx_info;
+ enum ieee80211_bss_changed bss_changed;
+ int error;
+
+ lhw = vap->iv_ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+ IEEE80211_UNLOCK(vap->iv_ic);
wiphy_lock(hw->wiphy);
+ LKPI_80211_LVIF_LOCK(lvif);
+ /*
+ * XXX-BZ KASSERT later; state going down so no action in theory
+ * but try to avoid a NULL-pointer derref for now and gracefully
+ * fail for non-debug kernels.
+ */
+ if (lvif->lvif_bss == NULL) {
+ ic_printf(vap->iv_ic, "%s:%d: ERROR: lvif %p vap %p iv_bss %p "
+ "lvif_bss %p lvif_bss->ni %p synched %d; "
+ "expect follow-up problems\n", __func__, __LINE__,
+ 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);
+ /*
+ * This will likely lead to a firmware crash (if there
+ * was not one before already) and need a
+ * ieee80211_restart_hw() but still better than a panic
+ * for users as they can at least recover.
+ */
+ error = ENOTRECOVERABLE;
+ goto out;
+ }
+ lsta = lvif->lvif_bss;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
+ "lvif %p vap %p\n", __func__,
+ lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
+ ni = lsta->ni; /* Reference held for lvif_bss. */
+ sta = LSTA_TO_STA(lsta);
+
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+ /* 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, false, true);
@@ -3436,8 +3259,8 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* End mgd_complete_tx. */
if (lsta->in_mgd) {
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.success = false;
- prep_tx_info.was_assoc = true;
+ prep_tx_info.subtype = IEEE80211_STYPE_AUTH;
+ prep_tx_info.link_id = 0;
lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
lsta->in_mgd = false;
}
@@ -3445,36 +3268,8 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* sync_rx_queues */
lkpi_80211_mo_sync_rx_queues(hw);
- /* sta_pre_rcu_remove */
- lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
-
- /* Take the station down. */
-
- /* Adjust sta and change state (from AUTHORIZED) to ASSOC. */
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
- KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not "
- "AUTHORIZED: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC);
- if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
#ifdef LKPI_80211_HW_CRYPTO
if (lkpi_hwcrypto) {
- /*
- * In theory we only need to do this if we changed assoc.
- * If we were not assoc, there should be no keys and we
- * should not be here.
- */
-#ifdef notyet
- KASSERT((bss_changed & BSS_CHANGED_ASSOC) != 0, ("%s: "
- "trying to remove keys but were not assoc: %#010jx, lvif %p\n",
- __func__, (uintmax_t)bss_changed, lvif));
-#endif
error = lkpi_sta_del_keys(hw, vif, lsta);
if (error != 0) {
ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys "
@@ -3489,50 +3284,36 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
}
#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 "
- "ASSOC: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH);
- if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
+ /* sta_pre_rcu_remove */
+ lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+ synchronize_net();
- /* Update sta and change state (from AUTH) to NONE. */
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
- KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
- "AUTH: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
- if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
+ /* Take the station down. */
bss_changed = 0;
/*
- * Start updating bss info (bss_info_changed) (assoc, aid, ..).
+ * 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 if not careful
- * (bss_info_changed() change is executed right away).
+ * One would expect this to happen when going off AUTHORIZED but
+ * not so.
*
- * 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.
+ * Immediately issuing the (*bss_info_changed) used to also remove the
+ * sta from firmware for iwlwifi; or we have problems with the sta
+ * silently not being removed and then crash upon the next sta add.
+ * Neither seems to be the case or a problem still.
*
- * 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()
+ * Contrary for BE200 (iwlwifi/mld) if we do not issue the
+ * (*vif_cfg_change) to tell FW that we are no longer assoc
+ * it will crash now upon sta rm. So the order now is as we once
+ * expected it:
+ *
+ * 1) lkpi_disassoc(): set vif->cfg.assoc = false and .aid=0
+ * 2) add the remaining BSS_CHANGED changes and call (*bss_info_changed)
+ * (which may be split up into (*vif_cfg_change) and
+ * (*link_info_changed) for more modern drivers).
+ * 3) call the last sta_state update -> IEEE80211_STA_NOTEXIST
+ * (removes the sta given assoc is false) and tidy up our lists.
* 4) call unassign_vif_chanctx
* 5) call lkpi_hw_conf_idle
* 6) call remove_chanctx
@@ -3544,6 +3325,18 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+ IMPROVE("Any bss_info changes to announce?");
+ vif->bss_conf.qos = false;
+ 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;
+ /* XXX BSS_CHANGED_???? */
+ vif->bss_conf.dtim_period = 0; /* go back to 0. */
+ bss_changed |= BSS_CHANGED_BEACON_INFO;
+ lkpi_bss_info_change(hw, vif, bss_changed);
+
/* Adjust sta and change state (from NONE) to NOTEXIST. */
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
@@ -3558,20 +3351,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
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?");
- 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_???? */
- vif->bss_conf.dtim_period = 0; /* go back to 0. */
- bss_changed |= BSS_CHANGED_BEACON_INFO;
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
LKPI_80211_LVIF_LOCK(lvif);
/* Remove ni reference for this cache of lsta. */
@@ -3583,11 +3363,10 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_remove_chanctx(hw, vif);
- error = EALREADY;
out:
wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
- if (error == EALREADY) {
+ if (error == 0) {
/*
* We do this outside the wiphy lock as net80211::node_free() may call
* into crypto code to delete keys and we have a recursed on
@@ -3599,15 +3378,156 @@ out:
*/
ieee80211_free_node(ni);
}
-outni:
+ return (error);
+}
+
+/* DOWN4 */
+static int
+lkpi_sta_scan_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ /* lkpi_iv_newstate() handles the stop scan case in common code. */
+ return (lkpi_sta_state_do_nada(vap, nstate, arg));
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+static int
+lkpi_sta_auth_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ int error;
+
+ error = lkpi_sta_auth_to_scan(vap, nstate, arg);
+ if (error == 0)
+ error = lkpi_sta_scan_to_init(vap, nstate, arg);
+ return (error);
+}
+
+/* auth_to_auth, assoc_to_assoc. */
+static int
+lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ struct lkpi_sta *lsta;
+ struct ieee80211_prep_tx_info prep_tx_info;
+ int error;
+
+ lhw = vap->iv_ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+
+ IEEE80211_UNLOCK(vap->iv_ic);
+ wiphy_lock(hw->wiphy);
+
+ LKPI_80211_LVIF_LOCK(lvif);
+ /* XXX-BZ KASSERT later? */
+ if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
+#ifdef LINUXKPI_DEBUG_80211
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched);
+#endif
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ error = ENOTRECOVERABLE;
+ goto out;
+ }
+ lsta = lvif->lvif_bss;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+
+ KASSERT(lsta != NULL, ("%s: lsta %p! lvif %p vap %p\n", __func__,
+ lsta, lvif, vap));
+
+ IMPROVE("event callback?");
+
+ /* End mgd_complete_tx. */
+ if (lsta->in_mgd) {
+ memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+ if (vap->iv_state == IEEE80211_S_AUTH)
+ prep_tx_info.subtype = IEEE80211_STYPE_AUTH;
+ else
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ prep_tx_info.link_id = 0;
+ lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
+ lsta->in_mgd = false;
+ }
+
+ /* Now start auth/assoc. */
+
+ /* Start mgd_prepare_tx. */
+ if (!lsta->in_mgd) {
+ memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+ if (nstate == IEEE80211_S_AUTH)
+ prep_tx_info.subtype = IEEE80211_STYPE_AUTH;
+ else
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ prep_tx_info.link_id = 0;
+ lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
+ lsta->in_mgd = true;
+ }
+
+ error = 0;
+out:
+ wiphy_unlock(hw->wiphy);
+ IEEE80211_LOCK(vap->iv_ic);
+
+ return (error);
+}
+
+static int
+lkpi_sta_assoc_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ int error;
+
+ error = lkpi_sta_assoc_to_auth(vap, nstate, arg);
+ if (error != 0 && error != EALREADY)
+ return (error);
+
+ error = lkpi_sta_auth_to_scan(vap, nstate, arg);
+ return (error);
+}
+
+static int
+lkpi_sta_assoc_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ int error;
+
+ error = lkpi_sta_assoc_to_scan(vap, nstate, arg);
+ if (error != 0 && error != EALREADY)
+ return (error);
+
+ error = lkpi_sta_scan_to_init(vap, nstate, arg); /* do_nada */
+ return (error);
+}
+
+static int
+lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ int error;
+
+ error = lkpi_sta_run_to_assoc(vap, nstate, arg);
+ if (error != 0 && error != EALREADY)
+ return (error);
+
+ error = lkpi_sta_assoc_to_init(vap, nstate, arg);
return (error);
}
static int
lkpi_sta_run_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ int error;
+
+ error = lkpi_sta_run_to_assoc(vap, nstate, arg);
+ if (error != 0 && error != EALREADY)
+ return (error);
- return (lkpi_sta_run_to_init(vap, nstate, arg));
+ error = lkpi_sta_assoc_to_scan(vap, nstate, arg);
+ return (error);
}
static int
@@ -3615,13 +3535,11 @@ lkpi_sta_run_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
{
int error;
- error = lkpi_sta_run_to_init(vap, nstate, arg);
+ error = lkpi_sta_run_to_assoc(vap, nstate, arg);
if (error != 0 && error != EALREADY)
return (error);
- /* At this point iv_bss is long a new node! */
-
- error |= lkpi_sta_scan_to_auth(vap, nstate, 0);
+ error = lkpi_sta_assoc_to_auth(vap, nstate, arg);
return (error);
}
@@ -3639,29 +3557,29 @@ struct fsm_state {
int (*handler)(struct ieee80211vap *, enum ieee80211_state, int);
} sta_state_fsm[] = {
{ IEEE80211_S_INIT, IEEE80211_S_INIT, lkpi_sta_state_do_nada },
- { IEEE80211_S_SCAN, IEEE80211_S_INIT, lkpi_sta_state_do_nada }, /* scan_to_init */
+ { IEEE80211_S_SCAN, IEEE80211_S_INIT, lkpi_sta_state_do_nada }, /* DOWN4 scan_to_init */
{ IEEE80211_S_AUTH, IEEE80211_S_INIT, lkpi_sta_auth_to_init }, /* not explicitly in sta_newstate() */
{ IEEE80211_S_ASSOC, IEEE80211_S_INIT, lkpi_sta_assoc_to_init }, /* Send DEAUTH. */
{ IEEE80211_S_RUN, IEEE80211_S_INIT, lkpi_sta_run_to_init }, /* Send DISASSOC. */
- { IEEE80211_S_INIT, IEEE80211_S_SCAN, lkpi_sta_state_do_nada },
+ { IEEE80211_S_INIT, IEEE80211_S_SCAN, lkpi_sta_init_to_scan }, /* UP1 */
{ IEEE80211_S_SCAN, IEEE80211_S_SCAN, lkpi_sta_state_do_nada },
- { IEEE80211_S_AUTH, IEEE80211_S_SCAN, lkpi_sta_auth_to_scan },
+ { IEEE80211_S_AUTH, IEEE80211_S_SCAN, lkpi_sta_auth_to_scan }, /* DOWN3 */
{ IEEE80211_S_ASSOC, IEEE80211_S_SCAN, lkpi_sta_assoc_to_scan },
{ IEEE80211_S_RUN, IEEE80211_S_SCAN, lkpi_sta_run_to_scan }, /* Beacon miss. */
{ IEEE80211_S_INIT, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, /* Send AUTH. */
- { IEEE80211_S_SCAN, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, /* Send AUTH. */
+ { IEEE80211_S_SCAN, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, /* UP2 Send AUTH. */
{ IEEE80211_S_AUTH, IEEE80211_S_AUTH, lkpi_sta_a_to_a }, /* Send ?AUTH. */
- { IEEE80211_S_ASSOC, IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth }, /* Send ?AUTH. */
+ { IEEE80211_S_ASSOC, IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth }, /* DOWN2 Send ?AUTH. */
{ IEEE80211_S_RUN, IEEE80211_S_AUTH, lkpi_sta_run_to_auth }, /* Send ?AUTH. */
- { IEEE80211_S_AUTH, IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc }, /* Send ASSOCREQ. */
+ { IEEE80211_S_AUTH, IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc }, /* UP3.1 Send ASSOCREQ. */
{ IEEE80211_S_ASSOC, IEEE80211_S_ASSOC, lkpi_sta_a_to_a }, /* Send ASSOCREQ. */
- { IEEE80211_S_RUN, IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc }, /* Send ASSOCREQ/REASSOCREQ. */
+ { IEEE80211_S_RUN, IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc }, /* DOWN1 Send ASSOCREQ/REASSOCREQ. */
- { IEEE80211_S_AUTH, IEEE80211_S_RUN, lkpi_sta_auth_to_run },
- { IEEE80211_S_ASSOC, IEEE80211_S_RUN, lkpi_sta_assoc_to_run },
+ { IEEE80211_S_AUTH, IEEE80211_S_RUN, lkpi_sta_auth_to_run }, /* UP3.2 */
+ { IEEE80211_S_ASSOC, IEEE80211_S_RUN, lkpi_sta_assoc_to_run }, /* UP4 */
{ IEEE80211_S_RUN, IEEE80211_S_RUN, lkpi_sta_state_do_nada },
/* Dummy at the end without handler. */
@@ -3703,7 +3621,7 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
} else {
ic_printf(vap->iv_ic, "%s: only station mode currently supported: "
- "cap %p iv_opmode %d\n", __func__, vap, vap->iv_opmode);
+ "vap %p iv_opmode %d\n", __func__, vap, vap->iv_opmode);
return (ENOSYS);
}
@@ -3797,7 +3715,7 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
struct chanAccParams chp;
struct wmeParams wmeparr[WME_NUM_AC];
struct ieee80211_tx_queue_params txqp;
- enum ieee80211_bss_changed changed;
+ enum ieee80211_bss_changed bss_changed;
int error;
uint16_t ac;
@@ -3848,11 +3766,11 @@ 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);
}
- changed = BSS_CHANGED_QOS;
+ bss_changed = BSS_CHANGED_QOS;
if (!planned)
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
+ lkpi_bss_info_change(hw, vif, bss_changed);
- return (changed);
+ return (bss_changed);
}
#endif
@@ -3918,7 +3836,7 @@ lkpi_iv_sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
* locking, see if queue_work() is fast enough.
*/
bss_changed = lkpi_update_dtim_tsf(vif, ni, ni->ni_vap, __func__, __LINE__);
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ lkpi_bss_info_change(hw, vif, bss_changed);
}
/*
@@ -3964,7 +3882,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
struct ieee80211vap *vap;
struct ieee80211_vif *vif;
struct ieee80211_tx_queue_params txqp;
- enum ieee80211_bss_changed changed;
+ enum ieee80211_bss_changed bss_changed;
struct sysctl_oid *node;
size_t len;
int error, i;
@@ -4081,8 +3999,8 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
/* Set bss_info. */
- changed = 0;
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
+ bss_changed = 0;
+ lkpi_bss_info_change(hw, vif, bss_changed);
/* 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");
@@ -4100,8 +4018,8 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
__func__, ac, error);
}
wiphy_unlock(hw->wiphy);
- changed = BSS_CHANGED_QOS;
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
+ bss_changed = BSS_CHANGED_QOS;
+ lkpi_bss_info_change(hw, vif, bss_changed);
/* Force MC init. */
lkpi_update_mcast_filter(ic);
@@ -4173,6 +4091,11 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
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");
+ SYSCTL_ADD_PROC(&lvif->sysctl_ctx,
+ SYSCTL_CHILDREN(node), OID_AUTO, "dump_stas_queues",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE | CTLFLAG_SKIP, lvif, 0,
+ lkpi_80211_dump_sta_queues, "A",
+ "Dump queue statistics for any sta of this vif");
IMPROVE();
@@ -5513,6 +5436,8 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
#endif
ni = lsta->ni;
+ ieee80211_output_seqno_assign(ni, -1, m);
+
k = NULL;
keyix = IEEE80211_KEYIX_NONE;
wh = mtod(m, struct ieee80211_frame *);
@@ -5641,8 +5566,10 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
c = ic->ic_curchan;
info->band = lkpi_net80211_chan_to_nl80211_band(c);
info->hw_queue = vif->hw_queue[ac];
- if (m->m_flags & M_EAPOL)
+ if ((m->m_flags & M_EAPOL) != 0) {
info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
+ info->flags |= IEEE80211_TX_CTL_USE_MINRATE; /* mt76 */
+ }
info->control.vif = vif;
/* XXX-BZ info->control.rates */
#ifdef __notyet__
@@ -5667,6 +5594,8 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
dev_kfree_skb(skb);
return;
}
+ /* Reset header as data might have moved. */
+ hdr = (void *)skb->data;
}
#endif
@@ -5690,6 +5619,7 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
LKPI_80211_LTXQ_LOCK(ltxq);
skb_queue_tail(&ltxq->skbq, skb);
+ ltxq->frms_enqueued++;
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_TX)
printf("%s:%d mo_wake_tx_queue :: %d %lu lsta %p sta %p "
@@ -5703,7 +5633,7 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
#endif
LKPI_80211_LTXQ_UNLOCK(ltxq);
wiphy_lock(hw->wiphy);
- lkpi_80211_mo_wake_tx_queue(hw, &ltxq->txq);
+ lkpi_80211_mo_wake_tx_queue(hw, &ltxq->txq, true);
wiphy_unlock(hw->wiphy);
return;
@@ -5719,6 +5649,7 @@ ops_tx:
control.sta = sta;
wiphy_lock(hw->wiphy);
lkpi_80211_mo_tx(hw, &control, skb);
+ lsta->frms_tx++;
wiphy_unlock(hw->wiphy);
}
@@ -6912,7 +6843,9 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
}
if (bootverbose) {
- ic_printf(ic, "netdev_features %b\n", hw->netdev_features, NETIF_F_BITS);
+ if (hw->netdev_features != 0)
+ ic_printf(ic, "netdev_features %b\n",
+ hw->netdev_features, NETIF_F_BITS);
ieee80211_announce(ic);
}
@@ -7008,6 +6941,14 @@ lkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
void *arg)
{
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ net80211_vap_printf(LVIF_TO_VAP(VIF_TO_LVIF(vif)),
+ "%s:%d: lsta %6D added_to_drv %d kc[keyix %u] %p\n",
+ __func__, __LINE__, LSTA_TO_STA(lsta)->addr, ":",
+ lsta->added_to_drv, keyix, lsta->kc[keyix]);
+#endif
+
if (!lsta->added_to_drv)
return;
@@ -7961,6 +7902,77 @@ linuxkpi_wiphy_free(struct wiphy *wiphy)
kfree(lwiphy);
}
+static void
+lkpi_wiphy_band_annotate(struct wiphy *wiphy)
+{
+ int band;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ struct ieee80211_supported_band *supband;
+ int i;
+
+ supband = wiphy->bands[band];
+ if (supband == NULL)
+ continue;
+
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ case NL80211_BAND_5GHZ:
+ break;
+ default:
+#ifdef LINUXKPI_DEBUG_80211
+ IMPROVE("band %d(%s) not yet supported",
+ band, lkpi_nl80211_band_name(band));
+ /* For bands added here, also check lkpi_lsta_alloc(). */
+#endif
+ continue;
+ }
+
+ for (i = 0; i < supband->n_bitrates; i++) {
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ switch (supband->bitrates[i].bitrate) {
+ case 110:
+ case 55:
+ case 20:
+ case 10:
+ supband->bitrates[i].flags |=
+ IEEE80211_RATE_MANDATORY_B;
+ /* FALLTHROUGH */
+ /* 11g only */
+ case 240:
+ case 120:
+ case 60:
+ supband->bitrates[i].flags |=
+ IEEE80211_RATE_MANDATORY_G;
+ break;
+ }
+ break;
+ case NL80211_BAND_5GHZ:
+ switch (supband->bitrates[i].bitrate) {
+ case 240:
+ case 120:
+ case 60:
+ supband->bitrates[i].flags |=
+ IEEE80211_RATE_MANDATORY_A;
+ break;
+ }
+ break;
+ }
+ }
+ }
+}
+
+int
+linuxkpi_80211_wiphy_register(struct wiphy *wiphy)
+{
+ TODO("Lots of checks and initialization");
+
+ lkpi_wiphy_band_annotate(wiphy);
+
+ return (0);
+}
+
static uint32_t
lkpi_cfg80211_calculate_bitrate_ht(struct rate_info *rate)
{
@@ -8121,6 +8133,8 @@ linuxkpi_ieee80211_tx_dequeue(struct ieee80211_hw *hw,
LKPI_80211_LTXQ_LOCK(ltxq);
skb = skb_dequeue(&ltxq->skbq);
+ if (skb != NULL)
+ ltxq->frms_dequeued++;
LKPI_80211_LTXQ_UNLOCK(ltxq);
stopped:
@@ -8167,6 +8181,9 @@ _lkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211_node *ni;
struct mbuf *m;
+ if (skb == NULL)
+ return;
+
m = skb->m;
skb->m = NULL;
@@ -8192,13 +8209,13 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw,
struct ieee80211_tx_status *txstat)
{
struct sk_buff *skb;
- struct ieee80211_tx_info *info;
+ struct ieee80211_tx_info *info, _info = { };
struct ieee80211_ratectl_tx_status txs;
struct ieee80211_node *ni;
int status;
skb = txstat->skb;
- if (skb->m != NULL) {
+ if (skb != NULL && skb->m != NULL) {
struct mbuf *m;
m = skb->m;
@@ -8208,7 +8225,13 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw,
ni = NULL;
}
+ /*
+ * If we have no info information on tx, set info to an all-zero struct
+ * to make the code (and debug output) simpler.
+ */
info = txstat->info;
+ if (info == NULL)
+ info = &_info;
if (info->flags & IEEE80211_TX_STAT_ACK) {
status = 0; /* No error. */
txs.status = IEEE80211_RATECTL_TX_SUCCESS;
@@ -8273,7 +8296,8 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw,
if (txstat->free_list) {
_lkpi_ieee80211_free_txskb(hw, skb, status);
- list_add_tail(&skb->list, txstat->free_list);
+ if (skb != NULL)
+ list_add_tail(&skb->list, txstat->free_list);
} else {
linuxkpi_ieee80211_free_txskb(hw, skb, status);
}
@@ -8614,7 +8638,7 @@ lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq)
ltxq->stopped = false;
if (!skb_queue_empty(&ltxq->skbq))
- lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
+ lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid], false);
}
}
rcu_read_unlock();
@@ -8663,6 +8687,43 @@ linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum)
spin_unlock_irqrestore(&lhw->txq_lock, flags);
}
+void
+linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct lkpi_hw *lhw;
+
+ lhw = HW_TO_LHW(hw);
+
+ LKPI_80211_LHW_TXQ_LOCK(lhw);
+ ieee80211_txq_schedule_start(hw, txq->ac);
+ do {
+ struct lkpi_txq *ltxq;
+ struct ieee80211_txq *ntxq;
+ struct ieee80211_tx_control control;
+ struct sk_buff *skb;
+
+ ntxq = ieee80211_next_txq(hw, txq->ac);
+ if (ntxq == NULL)
+ break;
+ ltxq = TXQ_TO_LTXQ(ntxq);
+
+ memset(&control, 0, sizeof(control));
+ control.sta = ntxq->sta;
+ do {
+ skb = linuxkpi_ieee80211_tx_dequeue(hw, ntxq);
+ if (skb == NULL)
+ break;
+ ltxq->frms_tx++;
+ lkpi_80211_mo_tx(hw, &control, skb);
+ } while(1);
+
+ ieee80211_return_txq(hw, ntxq, false);
+ } while (1);
+ ieee80211_txq_schedule_end(hw, txq->ac);
+ LKPI_80211_LHW_TXQ_UNLOCK(lhw);
+}
+
/* -------------------------------------------------------------------------- */
/* This is just hardware queues. */
@@ -8764,41 +8825,6 @@ out:
/* -------------------------------------------------------------------------- */
-void
-linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq)
-{
- struct lkpi_hw *lhw;
- struct ieee80211_txq *ntxq;
- struct ieee80211_tx_control control;
- struct sk_buff *skb;
-
- lhw = HW_TO_LHW(hw);
-
- LKPI_80211_LHW_TXQ_LOCK(lhw);
- ieee80211_txq_schedule_start(hw, txq->ac);
- do {
- ntxq = ieee80211_next_txq(hw, txq->ac);
- if (ntxq == NULL)
- break;
-
- memset(&control, 0, sizeof(control));
- control.sta = ntxq->sta;
- do {
- skb = linuxkpi_ieee80211_tx_dequeue(hw, ntxq);
- if (skb == NULL)
- break;
- lkpi_80211_mo_tx(hw, &control, skb);
- } while(1);
-
- ieee80211_return_txq(hw, ntxq, false);
- } while (1);
- ieee80211_txq_schedule_end(hw, txq->ac);
- LKPI_80211_LHW_TXQ_UNLOCK(lhw);
-}
-
-/* -------------------------------------------------------------------------- */
-
struct lkpi_cfg80211_bss {
u_int refcnt;
struct cfg80211_bss bss;
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h
index fcbef46fc6de..8ae5c3d13d2d 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.h
+++ b/sys/compat/linuxkpi/common/src/linux_80211.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2023 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
* Copyright (c) 2020-2021 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -154,6 +154,9 @@ struct lkpi_txq {
bool stopped;
uint32_t txq_generation;
struct sk_buff_head skbq;
+ uint64_t frms_enqueued;
+ uint64_t frms_dequeued;
+ uint64_t frms_tx;
/* Must be last! */
struct ieee80211_txq txq __aligned(CACHE_LINE_SIZE);
@@ -180,6 +183,7 @@ struct lkpi_sta {
bool in_mgd; /* XXX-BZ should this be per-vif? */
struct station_info sinfo; /* statistics */
+ uint64_t frms_tx; /* (*tx) */
/* Must be last! */
struct ieee80211_sta sta __aligned(CACHE_LINE_SIZE);
@@ -465,6 +469,10 @@ void lkpi_80211_mo_change_chanctx(struct ieee80211_hw *,
struct ieee80211_chanctx_conf *, uint32_t);
void lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *,
struct ieee80211_chanctx_conf *);
+void lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *, struct ieee80211_vif *,
+ uint64_t, bool);
+void lkpi_80211_mo_link_info_changed(struct ieee80211_hw *, struct ieee80211_vif *,
+ struct ieee80211_bss_conf *, uint64_t, uint8_t, bool);
void lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *, struct ieee80211_vif *,
struct ieee80211_bss_conf *, uint64_t);
int lkpi_80211_mo_conf_tx(struct ieee80211_hw *, struct ieee80211_vif *,
@@ -477,7 +485,8 @@ void lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *, struct ieee80211_vif *
struct ieee80211_prep_tx_info *);
void lkpi_80211_mo_tx(struct ieee80211_hw *, struct ieee80211_tx_control *,
struct sk_buff *);
-void lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *, struct ieee80211_txq *);
+void lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *, struct ieee80211_txq *,
+ bool);
void lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *);
void lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *,
struct ieee80211_vif *, struct ieee80211_sta *);
diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
index a85e6d0d0dd7..42067e36c953 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211_macops.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2021-2022 The FreeBSD Foundation
+ * Copyright (c) 2021-2026 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -546,24 +546,80 @@ lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw,
}
void
-lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *conf, uint64_t changed)
+lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ uint64_t vif_cfg_bits, bool fallback)
{
struct lkpi_hw *lhw;
+ might_sleep();
+ /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
+
+ lhw = HW_TO_LHW(hw);
+ if (lhw->ops->vif_cfg_changed == NULL &&
+ lhw->ops->bss_info_changed == NULL)
+ return;
+
+ if (vif_cfg_bits == 0)
+ return;
+
+ LKPI_80211_TRACE_MO("hw %p vif %p vif_cfg_bits %#jx", hw, vif, (uintmax_t)vif_cfg_bits);
+ if (lhw->ops->link_info_changed != NULL)
+ lhw->ops->vif_cfg_changed(hw, vif, vif_cfg_bits);
+ else if (fallback)
+ lhw->ops->bss_info_changed(hw, vif, &vif->bss_conf, vif_cfg_bits);
+}
+
+void
+lkpi_80211_mo_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf, uint64_t link_info_bits, uint8_t link_id,
+ bool fallback)
+{
+ struct lkpi_hw *lhw;
+
+ might_sleep();
+ /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
+
lhw = HW_TO_LHW(hw);
if (lhw->ops->link_info_changed == NULL &&
lhw->ops->bss_info_changed == NULL)
return;
- if (changed == 0)
+ if (link_info_bits == 0)
return;
- LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed);
+ if (!ieee80211_vif_link_active(vif, link_id))
+ return;
+
+ LKPI_80211_TRACE_MO("hw %p vif %p conf %p link_info_bits %#jx", hw, vif, conf, (uintmax_t)link_info_bits);
if (lhw->ops->link_info_changed != NULL)
- lhw->ops->link_info_changed(hw, vif, conf, changed);
- else
- lhw->ops->bss_info_changed(hw, vif, conf, changed);
+ lhw->ops->link_info_changed(hw, vif, conf, link_info_bits);
+ else if (fallback)
+ lhw->ops->bss_info_changed(hw, vif, conf, link_info_bits);
+}
+
+/*
+ * This is basically obsolete but one caller.
+ * The functionality is now split between lkpi_80211_mo_link_info_changed() and
+ * lkpi_80211_mo_vif_cfg_changed(). Those functions have a flag whether to call
+ * the (*bss_info_changed) fallback or not. See lkpi_bss_info_change().
+ */
+void
+lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf, uint64_t bss_changed)
+{
+ struct lkpi_hw *lhw;
+
+ /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
+
+ lhw = HW_TO_LHW(hw);
+ if (lhw->ops->bss_info_changed == NULL)
+ return;
+
+ if (bss_changed == 0)
+ return;
+
+ LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)bss_changed);
+ lhw->ops->bss_info_changed(hw, vif, conf, bss_changed);
}
int
@@ -644,11 +700,17 @@ lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl,
}
void
-lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
+lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
+ bool schedule)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
+
+ /* Do the schedule before the check for wake_tx_queue supported! */
+ if (schedule)
+ ieee80211_schedule_txq(hw, txq);
+
if (lhw->ops->wake_tx_queue == NULL)
return;
diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c
index 35430daf311d..8fc644241d79 100644
--- a/sys/compat/linuxkpi/common/src/linux_compat.c
+++ b/sys/compat/linuxkpi/common/src/linux_compat.c
@@ -1894,56 +1894,168 @@ 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)
+ const void *buf, size_t len, const bool ascii, const bool trailing_newline)
{
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;
+ int row, linelen, ret;
while (len > 0) {
- if (level != NULL)
- _fpf(arg1, "%s", level);
- if (prefix_str != NULL)
- _fpf(arg1, "%s ", prefix_str);
+ linelen = 0;
+ if (level != NULL) {
+ ret = _fpf(arg1, "%s", level);
+ if (ret < 0)
+ break;
+ linelen += ret;
+ }
+ if (prefix_str != NULL) {
+ ret = _fpf(
+ arg1, "%s%s", linelen ? " " : "", prefix_str);
+ if (ret < 0)
+ break;
+ linelen += ret;
+ }
switch (prefix_type) {
case DUMP_PREFIX_ADDRESS:
- _fpf(arg1, "[%p] ", buf);
+ ret = _fpf(
+ arg1, "%s[%p]", linelen ? " " : "", buf);
+ if (ret < 0)
+ return;
+ linelen += ret;
break;
case DUMP_PREFIX_OFFSET:
- _fpf(arg1, "[%#tx] ", ((const char *)buf -
- (const char *)buf_old));
+ ret = _fpf(
+ arg1, "%s[%#tx]", linelen ? " " : "",
+ ((const char *)buf - (const char *)buf_old));
+ if (ret < 0)
+ return;
+ linelen += ret;
break;
default:
break;
}
for (row = 0; row != rowsize; row++) {
if (groupsize == 8 && len > 7) {
- _fpf(arg1, "%016llx ", ((print_64p_t)buf)->value);
+ ret = _fpf(
+ arg1, "%s%016llx", linelen ? " " : "",
+ ((print_64p_t)buf)->value);
+ if (ret < 0)
+ return;
+ linelen += ret;
buf = (const uint8_t *)buf + 8;
len -= 8;
} else if (groupsize == 4 && len > 3) {
- _fpf(arg1, "%08x ", ((print_32p_t)buf)->value);
+ ret = _fpf(
+ arg1, "%s%08x", linelen ? " " : "",
+ ((print_32p_t)buf)->value);
+ if (ret < 0)
+ return;
+ linelen += ret;
buf = (const uint8_t *)buf + 4;
len -= 4;
} else if (groupsize == 2 && len > 1) {
- _fpf(arg1, "%04x ", ((print_16p_t)buf)->value);
+ ret = _fpf(
+ arg1, "%s%04x", linelen ? " " : "",
+ ((print_16p_t)buf)->value);
+ if (ret < 0)
+ return;
+ linelen += ret;
buf = (const uint8_t *)buf + 2;
len -= 2;
} else if (len > 0) {
- _fpf(arg1, "%02x ", *(const uint8_t *)buf);
+ ret = _fpf(
+ arg1, "%s%02x", linelen ? " " : "",
+ *(const uint8_t *)buf);
+ if (ret < 0)
+ return;
+ linelen += ret;
buf = (const uint8_t *)buf + 1;
len--;
} else {
break;
}
}
- _fpf(arg1, "\n");
+ if (len > 0 && trailing_newline) {
+ ret = _fpf(arg1, "\n");
+ if (ret < 0)
+ break;
+ }
}
}
+struct hdtb_context {
+ char *linebuf;
+ size_t linebuflen;
+ int written;
+};
+
+static int
+hdtb_cb(void *arg, const char *format, ...)
+{
+ struct hdtb_context *context;
+ int written;
+ va_list args;
+
+ context = arg;
+
+ va_start(args, format);
+ written = vsnprintf(
+ context->linebuf, context->linebuflen, format, args);
+ va_end(args);
+
+ if (written < 0)
+ return (written);
+
+ /*
+ * Linux' hex_dump_to_buffer() function has the same behaviour as
+ * snprintf() basically. Therefore, it returns the number of bytes it
+ * would have written if the destination buffer was large enough.
+ *
+ * If the destination buffer was exhausted, lkpi_hex_dump() will
+ * continue to call this callback but it will only compute the bytes it
+ * would have written but write nothing to that buffer.
+ */
+ context->written += written;
+
+ if (written < context->linebuflen) {
+ context->linebuf += written;
+ context->linebuflen -= written;
+ } else {
+ context->linebuf += context->linebuflen;
+ context->linebuflen = 0;
+ }
+
+ return (written);
+}
+
+int
+lkpi_hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
+ int groupsize, char *linebuf, size_t linebuflen, bool ascii)
+{
+ int written;
+ struct hdtb_context context;
+
+ context.linebuf = linebuf;
+ context.linebuflen = linebuflen;
+ context.written = 0;
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ len = min(len, rowsize);
+
+ lkpi_hex_dump(
+ hdtb_cb, &context, NULL, NULL, DUMP_PREFIX_NONE,
+ rowsize, groupsize, buf, len, ascii, false);
+
+ written = context.written;
+
+ return (written);
+}
+
static void
linux_timer_callback_wrapper(void *context)
{
diff --git a/sys/compat/linuxkpi/common/src/linux_current.c b/sys/compat/linuxkpi/common/src/linux_current.c
index c342eb279caa..3bc5d31d211a 100644
--- a/sys/compat/linuxkpi/common/src/linux_current.c
+++ b/sys/compat/linuxkpi/common/src/linux_current.c
@@ -90,11 +90,8 @@ linux_alloc_current(struct thread *td, int flags)
}
ts = uma_zalloc(linux_current_zone, flags | M_ZERO);
- if (ts == NULL) {
- if ((flags & (M_WAITOK | M_NOWAIT)) == M_WAITOK)
- panic("linux_alloc_current: failed to allocate task");
+ if (ts == NULL)
return (ENOMEM);
- }
mm = NULL;
/* setup new task structure */
@@ -118,10 +115,7 @@ linux_alloc_current(struct thread *td, int flags)
PROC_UNLOCK(proc);
mm = uma_zalloc(linux_mm_zone, flags | M_ZERO);
if (mm == NULL) {
- if ((flags & (M_WAITOK | M_NOWAIT)) == M_WAITOK)
- panic(
- "linux_alloc_current: failed to allocate mm");
- uma_zfree(linux_current_zone, mm);
+ uma_zfree(linux_current_zone, ts);
return (ENOMEM);
}
diff --git a/sys/compat/linuxkpi/common/src/linux_eventfd.c b/sys/compat/linuxkpi/common/src/linux_eventfd.c
new file mode 100644
index 000000000000..126c6c54b9a5
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_eventfd.c
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2025 The FreeBSD Foundation
+ * Copyright (c) 2025 Jean-Sébastien Pédron
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+
+#include <linux/eventfd.h>
+
+struct eventfd_ctx *
+lkpi_eventfd_ctx_fdget(int fd)
+{
+ struct file *fp;
+ struct eventfd_ctx *ctx;
+
+ /* Lookup file pointer by file descriptor index. */
+ if (fget_unlocked(curthread, fd, &cap_no_rights, &fp) != 0)
+ return (ERR_PTR(-EBADF));
+
+ /*
+ * eventfd_get() bumps the refcount, so we can safely release the
+ * reference on the file itself afterwards.
+ */
+ ctx = eventfd_get(fp);
+ fdrop(fp, curthread);
+
+ if (ctx == NULL)
+ return (ERR_PTR(-EBADF));
+
+ return (ctx);
+}
+
+void
+lkpi_eventfd_ctx_put(struct eventfd_ctx *ctx)
+{
+ eventfd_put(ctx);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_firmware.c b/sys/compat/linuxkpi/common/src/linux_firmware.c
index 12658df5ce83..0c6d855501ef 100644
--- a/sys/compat/linuxkpi/common/src/linux_firmware.c
+++ b/sys/compat/linuxkpi/common/src/linux_firmware.c
@@ -66,7 +66,8 @@ _linuxkpi_request_firmware(const char *fw_name, const struct linuxkpi_firmware *
uint32_t flags;
if (fw_name == NULL || fw == NULL || dev == NULL) {
- *fw = NULL;
+ if (fw != NULL)
+ *fw = NULL;
return (-EINVAL);
}
diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c
index df5255d8a6ca..477cb321ea9e 100644
--- a/sys/compat/linuxkpi/common/src/linux_pci.c
+++ b/sys/compat/linuxkpi/common/src/linux_pci.c
@@ -385,14 +385,16 @@ lkpifill_pci_dev(device_t dev, struct pci_dev *pdev)
pdev->dev.bsddev = dev;
pdev->dev.parent = &linux_root_device;
pdev->dev.release = lkpi_pci_dev_release;
- INIT_LIST_HEAD(&pdev->dev.irqents);
if (pci_msi_count(dev) > 0)
pdev->msi_desc = malloc(pci_msi_count(dev) *
sizeof(*pdev->msi_desc), M_DEVBUF, M_WAITOK | M_ZERO);
+ TAILQ_INIT(&pdev->mmio);
+ spin_lock_init(&pdev->pcie_cap_lock);
spin_lock_init(&pdev->dev.devres_lock);
INIT_LIST_HEAD(&pdev->dev.devres_head);
+ INIT_LIST_HEAD(&pdev->dev.irqents);
return (0);
}
@@ -613,9 +615,6 @@ linux_pci_attach_device(device_t dev, struct pci_driver *pdrv,
if (error)
goto out_dma_init;
- TAILQ_INIT(&pdev->mmio);
- spin_lock_init(&pdev->pcie_cap_lock);
-
spin_lock(&pci_lock);
list_add(&pdev->links, &pci_devices);
spin_unlock(&pci_lock);
@@ -1720,9 +1719,26 @@ lkpi_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len,
}
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);
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) != 0)
+ goto skip_sync;
+ /* dma_sync_single_for_cpu() unrolled to avoid lock recursicn. */
+ switch (direction) {
+ case DMA_BIDIRECTIONAL:
+ bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_PREREAD);
+ break;
+ case DMA_TO_DEVICE:
+ bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_POSTWRITE);
+ break;
+ case DMA_FROM_DEVICE:
+ bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_POSTREAD);
+ break;
+ default:
+ break;
+ }
+
+skip_sync:
bus_dmamap_unload(obj->dmat, obj->dmamap);
bus_dmamap_destroy(obj->dmat, obj->dmamap);
DMA_PRIV_UNLOCK(priv);
@@ -1796,6 +1812,42 @@ lkpi_dmam_free_coherent(struct device *dev, void *p)
dma_free_coherent(dev, dr->size, dr->mem, *dr->handle);
}
+static int
+lkpi_dmam_coherent_match(struct device *dev, void *dr, void *mp)
+{
+ struct lkpi_devres_dmam_coherent *a, *b;
+
+ a = dr;
+ b = mp;
+
+ if (a->mem != b->mem)
+ return (0);
+ if (a->size != b->size || a->handle != b->handle)
+ dev_WARN(dev, "for mem %p: size %zu != %zu || handle %#jx != %#jx\n",
+ a->mem, a->size, b->size,
+ (uintmax_t)a->handle, (uintmax_t)b->handle);
+ return (1);
+}
+
+void
+linuxkpi_dmam_free_coherent(struct device *dev, size_t size,
+ void *addr, dma_addr_t dma_handle)
+{
+ struct lkpi_devres_dmam_coherent match = {
+ .size = size,
+ .handle = &dma_handle,
+ .mem = addr
+ };
+ int error;
+
+ error = devres_destroy(dev, lkpi_dmam_free_coherent,
+ lkpi_dmam_coherent_match, &match);
+ if (error != 0)
+ dev_WARN(dev, "devres_destroy returned %d, size %zu addr %p "
+ "dma_handle %#jx\n", error, size, addr, (uintmax_t)dma_handle);
+ dma_free_coherent(dev, size, addr, dma_handle);
+}
+
void *
linuxkpi_dmam_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t flag)
diff --git a/sys/compat/linuxkpi/common/src/linux_radix.c b/sys/compat/linuxkpi/common/src/linux_radix.c
index ee6b3a63c370..760f583f25c8 100644
--- a/sys/compat/linuxkpi/common/src/linux_radix.c
+++ b/sys/compat/linuxkpi/common/src/linux_radix.c
@@ -52,6 +52,81 @@ radix_pos(long id, int height)
return (id >> (RADIX_TREE_MAP_SHIFT * height)) & RADIX_TREE_MAP_MASK;
}
+static inline int
+root_tag_get(const struct radix_tree_root *root, unsigned tag)
+{
+ return (test_bit(tag, root->tags));
+}
+
+static inline void
+root_tag_set(struct radix_tree_root *root, unsigned tag)
+{
+ set_bit(tag, root->tags);
+}
+
+static inline void
+root_tag_clear(struct radix_tree_root *root, unsigned tag)
+{
+ clear_bit(tag, root->tags);
+}
+
+static inline int
+tag_get(const struct radix_tree_node *node, unsigned int tag, int pos)
+{
+ return (test_bit(pos, node->tags[tag]));
+}
+
+static inline void
+tag_set(struct radix_tree_node *node, unsigned int tag, int pos)
+{
+ set_bit(pos, node->tags[tag]);
+}
+
+static inline void
+tag_clear(struct radix_tree_node *node, unsigned int tag, int pos)
+{
+ clear_bit(pos, node->tags[tag]);
+}
+
+static inline bool
+any_tag_set(const struct radix_tree_node *node, unsigned int tag)
+{
+ unsigned int pos;
+
+ for (pos = 0; pos < RADIX_TREE_TAG_LONGS; pos++)
+ if (node->tags[tag][pos])
+ return (true);
+
+ return (false);
+}
+
+static void
+node_tag_clear(struct radix_tree_root *root, struct radix_tree_node *stack[],
+ unsigned long index, unsigned int tag)
+{
+ struct radix_tree_node *node;
+ int height, pos;
+
+ height = 1;
+ node = stack[height];
+
+ while (node && height <= root->height - 1) {
+ pos = radix_pos(index, height);
+ if (!tag_get(node, tag, pos))
+ return;
+
+ tag_clear(node, tag, pos);
+ if (any_tag_set(node, tag))
+ return;
+
+ height++;
+ node = stack[height];
+ }
+
+ if (root_tag_get(root, tag))
+ root_tag_clear(root, tag);
+}
+
static void
radix_tree_clean_root_node(struct radix_tree_root *root)
{
@@ -84,14 +159,70 @@ out:
return (item);
}
+unsigned int
+radix_tree_gang_lookup(const struct radix_tree_root *root, void **results,
+ unsigned long first_index, unsigned int max_items)
+{
+ struct radix_tree_iter iter;
+ void **slot;
+ int count;
+
+ count = 0;
+ if (max_items == 0)
+ return (count);
+
+ radix_tree_for_each_slot(slot, root, &iter, first_index) {
+ results[count] = *slot;
+ if (results[count] == NULL)
+ continue;
+
+ count++;
+ if (count == max_items)
+ break;
+ }
+
+ return (count);
+}
+
+unsigned int
+radix_tree_gang_lookup_tag(const struct radix_tree_root *root,
+ void **results, unsigned long first_index, unsigned int max_items,
+ unsigned int tag)
+{
+ struct radix_tree_iter iter;
+ void **slot;
+ int count;
+
+ count = 0;
+ if (max_items == 0)
+ return (count);
+
+ radix_tree_for_each_slot_tagged(slot, root, &iter, first_index, tag) {
+ results[count] = *slot;
+ if (results[count] == NULL)
+ continue;
+
+ count++;
+ if (count == max_items)
+ break;
+ }
+
+ return (count);
+}
+
bool
radix_tree_iter_find(const struct radix_tree_root *root,
- struct radix_tree_iter *iter, void ***pppslot)
+ struct radix_tree_iter *iter, void ***pppslot, int flags)
{
struct radix_tree_node *node;
unsigned long index = iter->index;
+ unsigned int tag;
int height;
+ tag = flags & RADIX_TREE_ITER_TAG_MASK;
+ if ((flags & RADIX_TREE_ITER_TAGGED) && !root_tag_get(root, tag))
+ return (false);
+
restart:
node = root->rnode;
if (node == NULL)
@@ -109,7 +240,9 @@ restart:
*pppslot = node->slots + pos;
next = node->slots[pos];
- if (next == NULL) {
+ if (next == NULL ||
+ (flags & RADIX_TREE_ITER_TAGGED &&
+ !tag_get(next, tag, pos))) {
index += step;
index &= -step;
if ((index & mask) == 0)
@@ -131,6 +264,7 @@ radix_tree_delete(struct radix_tree_root *root, unsigned long index)
void *item;
int height;
int idx;
+ int tag;
item = NULL;
node = root->rnode;
@@ -144,9 +278,15 @@ radix_tree_delete(struct radix_tree_root *root, unsigned long index)
stack[height] = node;
node = node->slots[radix_pos(index, height--)];
}
- idx = radix_pos(index, 0);
- if (node)
+
+ if (node) {
+ idx = radix_pos(index, 0);
item = node->slots[idx];
+
+ for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++)
+ node_tag_clear(root, stack, index, tag);
+ }
+
/*
* If we removed something reduce the height of the tree.
*/
@@ -380,3 +520,74 @@ radix_tree_store(struct radix_tree_root *root, unsigned long index, void **ppite
node->count++;
return (0);
}
+
+void *
+radix_tree_tag_set(struct radix_tree_root *root, unsigned long index,
+ unsigned int tag)
+{
+ struct radix_tree_node *node;
+ void *item;
+ int height, pos;
+
+ item = NULL;
+ node = root->rnode;
+ height = root->height - 1;
+ if (index > radix_max(root))
+ goto out;
+
+ while (height) {
+ KASSERT(
+ node != NULL,
+ ("Node at index %lu does not exist in radix tree %p",
+ index, root));
+
+ pos = radix_pos(index, height--);
+ node = node->slots[pos];
+
+ if (!tag_get(node, tag, pos))
+ tag_set(node, tag, pos);
+ }
+
+ item = node->slots[radix_pos(index, 0)];
+
+ root_tag_set(root, tag);
+
+out:
+ return (item);
+}
+
+void *
+radix_tree_tag_clear(struct radix_tree_root *root,
+ unsigned long index, unsigned int tag)
+{
+ struct radix_tree_node *stack[RADIX_TREE_MAX_HEIGHT];
+ struct radix_tree_node *node;
+ void *item;
+ int height;
+
+ item = NULL;
+ node = root->rnode;
+ height = root->height - 1;
+ if (index > radix_max(root))
+ goto out;
+
+ while (height && node) {
+ stack[height] = node;
+ node = node->slots[radix_pos(index, height--)];
+ }
+
+ if (node) {
+ item = node->slots[radix_pos(index, 0)];
+
+ node_tag_clear(root, stack, index, tag);
+ }
+
+out:
+ return (item);
+}
+
+int
+radix_tree_tagged(const struct radix_tree_root *root, unsigned int tag)
+{
+ return (root_tag_get(root, tag));
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_seq_buf.c b/sys/compat/linuxkpi/common/src/linux_seq_buf.c
new file mode 100644
index 000000000000..112c53044c22
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_seq_buf.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <linux/seq_buf.h>
+
+void
+linuxkpi_seq_buf_init(struct seq_buf *s, char *buf, unsigned int size)
+{
+ s->buffer = buf;
+ s->size = size;
+
+ seq_buf_clear(s);
+}
+
+int
+linuxkpi_seq_buf_printf(struct seq_buf *s, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, fmt);
+ ret = seq_buf_vprintf(s, fmt, args);
+ va_end(args);
+
+ return (ret);
+}
+
+int
+linuxkpi_seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
+{
+ int ret;
+
+ if (!seq_buf_has_overflowed(s)) {
+ ret = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
+ if (s->len + ret < s->size) {
+ s->len += ret;
+ return (0);
+ }
+ }
+
+ seq_buf_set_overflow(s);
+ return (-1);
+}
+
+const char *
+linuxkpi_seq_buf_str(struct seq_buf *s)
+{
+ if (s->size == 0)
+ return ("");
+
+ if (seq_buf_buffer_left(s))
+ s->buffer[s->len] = '\0';
+ else
+ s->buffer[s->size - 1] = '\0';
+
+ return (s->buffer);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_seq_file.c b/sys/compat/linuxkpi/common/src/linux_seq_file.c
index 9c06fe27bebe..eae414ea696e 100644
--- a/sys/compat/linuxkpi/common/src/linux_seq_file.c
+++ b/sys/compat/linuxkpi/common/src/linux_seq_file.c
@@ -40,7 +40,7 @@
MALLOC_DEFINE(M_LSEQ, "seq_file", "seq_file");
ssize_t
-seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos)
+seq_read(struct linux_file *f, char __user *ubuf, size_t size, off_t *ppos)
{
struct seq_file *m;
struct sbuf *sbuf;
@@ -49,6 +49,7 @@ seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos)
m = f->private_data;
sbuf = m->buf;
+ sbuf_clear(sbuf);
p = m->op->start(m, ppos);
rc = m->op->show(m, p);
@@ -59,15 +60,8 @@ seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos)
if (rc)
return (rc);
- rc = sbuf_len(sbuf);
- if (*ppos >= rc || size < 1)
- return (-EINVAL);
-
- size = min(rc - *ppos, size);
- memcpy(ubuf, sbuf_data(sbuf) + *ppos, size);
- *ppos += size;
-
- return (size);
+ return (simple_read_from_buffer(ubuf, size, ppos, sbuf_data(sbuf),
+ sbuf_len(sbuf)));
}
int
diff --git a/sys/compat/linuxkpi/common/src/linux_shmemfs.c b/sys/compat/linuxkpi/common/src/linux_shmemfs.c
index 1fb17bc5c0cb..d5c118ba7624 100644
--- a/sys/compat/linuxkpi/common/src/linux_shmemfs.c
+++ b/sys/compat/linuxkpi/common/src/linux_shmemfs.c
@@ -62,11 +62,10 @@ linux_shmem_read_mapping_page_gfp(vm_object_t obj, int pindex, gfp_t gfp)
struct linux_file *
linux_shmem_file_setup(const char *name, loff_t size, unsigned long flags)
{
- struct fileobj {
+ struct {
struct linux_file file __aligned(sizeof(void *));
struct vnode vnode __aligned(sizeof(void *));
- };
- struct fileobj *fileobj;
+ } *fileobj;
struct linux_file *filp;
struct vnode *vp;
int error;
@@ -89,7 +88,7 @@ linux_shmem_file_setup(const char *name, loff_t size, unsigned long flags)
}
return (filp);
err_1:
- kfree(filp);
+ kfree(fileobj);
err_0:
return (ERR_PTR(error));
}
diff --git a/sys/compat/linuxkpi/common/src/linux_simple_attr.c b/sys/compat/linuxkpi/common/src/linux_simple_attr.c
index 8cc9ec7ecbc9..e5514194cb33 100644
--- a/sys/compat/linuxkpi/common/src/linux_simple_attr.c
+++ b/sys/compat/linuxkpi/common/src/linux_simple_attr.c
@@ -99,7 +99,8 @@ simple_attr_release(struct inode *inode, struct file *filp)
* On failure, negative signed ERRNO
*/
ssize_t
-simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos)
+simple_attr_read(struct file *filp, char __user *buf, size_t read_size,
+ loff_t *ppos)
{
struct simple_attr *sattr;
uint64_t data;
@@ -119,18 +120,9 @@ simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos)
scnprintf(prebuf, sizeof(prebuf), sattr->fmt, data);
- ret = strlen(prebuf) + 1;
- if (*ppos >= ret || read_size < 1) {
- ret = -EINVAL;
- goto unlock;
- }
-
- read_size = min(ret - *ppos, read_size);
- ret = strscpy(buf, prebuf + *ppos, read_size);
-
/* add 1 for null terminator */
- if (ret > 0)
- ret += 1;
+ ret = simple_read_from_buffer(buf, read_size, ppos, prebuf,
+ strlen(prebuf) + 1);
unlock:
mutex_unlock(&sattr->mutex);
@@ -155,29 +147,38 @@ unlock:
* On failure, negative signed ERRNO
*/
static ssize_t
-simple_attr_write_common(struct file *filp, const char *buf, size_t write_size,
- loff_t *ppos, bool is_signed)
+simple_attr_write_common(struct file *filp, const char __user *ubuf,
+ size_t write_size, loff_t *ppos, bool is_signed)
{
struct simple_attr *sattr;
unsigned long long data;
- size_t bufsize;
+ char *buf;
ssize_t ret;
sattr = filp->private_data;
- bufsize = strlen(buf) + 1;
if (sattr->set == NULL)
return (-EFAULT);
- if (*ppos >= bufsize || write_size < 1)
+ if (*ppos != 0 || write_size < 1)
return (-EINVAL);
+ buf = malloc(write_size, M_LSATTR, M_WAITOK);
+ if (copy_from_user(buf, ubuf, write_size) != 0) {
+ free(buf, M_LSATTR);
+ return (-EFAULT);
+ }
+ if (strnlen(buf, write_size) == write_size) {
+ free(buf, M_LSATTR);
+ return (-EINVAL);
+ }
+
mutex_lock(&sattr->mutex);
if (is_signed)
- ret = kstrtoll(buf + *ppos, 0, &data);
+ ret = kstrtoll(buf, 0, &data);
else
- ret = kstrtoull(buf + *ppos, 0, &data);
+ ret = kstrtoull(buf, 0, &data);
if (ret)
goto unlock;
@@ -185,23 +186,24 @@ simple_attr_write_common(struct file *filp, const char *buf, size_t write_size,
if (ret)
goto unlock;
- ret = bufsize - *ppos;
+ ret = write_size;
unlock:
mutex_unlock(&sattr->mutex);
+ free(buf, M_LSATTR);
return (ret);
}
ssize_t
-simple_attr_write(struct file *filp, const char *buf, size_t write_size,
+simple_attr_write(struct file *filp, const char __user *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)
+simple_attr_write_signed(struct file *filp, const char __user *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_siphash.c b/sys/compat/linuxkpi/common/src/linux_siphash.c
new file mode 100644
index 000000000000..b4842a8250e1
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_siphash.c
@@ -0,0 +1,546 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+/* Copyright (C) 2016-2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * SipHash: a fast short-input PRF
+ * https://131002.net/siphash/
+ *
+ * This implementation is specifically for SipHash2-4 for a secure PRF
+ * and HalfSipHash1-3/SipHash1-3 for an insecure PRF only suitable for
+ * hashtables.
+ */
+
+#include <linux/siphash.h>
+#include <asm/unaligned.h>
+
+#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64
+#include <linux/dcache.h>
+#include <asm/word-at-a-time.h>
+#endif
+
+#define EXPORT_SYMBOL(name)
+
+#define SIPROUND SIPHASH_PERMUTATION(v0, v1, v2, v3)
+
+#define PREAMBLE(len) \
+ u64 v0 = SIPHASH_CONST_0; \
+ u64 v1 = SIPHASH_CONST_1; \
+ u64 v2 = SIPHASH_CONST_2; \
+ u64 v3 = SIPHASH_CONST_3; \
+ u64 b = ((u64)(len)) << 56; \
+ v3 ^= key->key[1]; \
+ v2 ^= key->key[0]; \
+ v1 ^= key->key[1]; \
+ v0 ^= key->key[0];
+
+#define POSTAMBLE \
+ v3 ^= b; \
+ SIPROUND; \
+ SIPROUND; \
+ v0 ^= b; \
+ v2 ^= 0xff; \
+ SIPROUND; \
+ SIPROUND; \
+ SIPROUND; \
+ SIPROUND; \
+ return (v0 ^ v1) ^ (v2 ^ v3);
+
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+u64 __siphash_aligned(const void *_data, size_t len, const siphash_key_t *key)
+{
+ const u8 *data = _data;
+ const u8 *end = data + len - (len % sizeof(u64));
+ const u8 left = len & (sizeof(u64) - 1);
+ u64 m;
+ PREAMBLE(len)
+ for (; data != end; data += sizeof(u64)) {
+ m = le64_to_cpup(data);
+ v3 ^= m;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= m;
+ }
+#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64
+ if (left)
+ b |= le64_to_cpu((__force __le64)(load_unaligned_zeropad(data) &
+ bytemask_from_count(left)));
+#else
+ switch (left) {
+ case 7: b |= ((u64)end[6]) << 48; fallthrough;
+ case 6: b |= ((u64)end[5]) << 40; fallthrough;
+ case 5: b |= ((u64)end[4]) << 32; fallthrough;
+ case 4: b |= le32_to_cpup(data); break;
+ case 3: b |= ((u64)end[2]) << 16; fallthrough;
+ case 2: b |= le16_to_cpup(data); break;
+ case 1: b |= end[0];
+ }
+#endif
+ POSTAMBLE
+}
+EXPORT_SYMBOL(__siphash_aligned);
+#endif
+
+u64 __siphash_unaligned(const void *_data, size_t len, const siphash_key_t *key)
+{
+ const u8 *data = _data;
+ const u8 *end = data + len - (len % sizeof(u64));
+ const u8 left = len & (sizeof(u64) - 1);
+ u64 m;
+ PREAMBLE(len)
+ for (; data != end; data += sizeof(u64)) {
+ m = get_unaligned_le64(data);
+ v3 ^= m;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= m;
+ }
+#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64
+ if (left)
+ b |= le64_to_cpu((__force __le64)(load_unaligned_zeropad(data) &
+ bytemask_from_count(left)));
+#else
+ switch (left) {
+ case 7: b |= ((u64)end[6]) << 48; fallthrough;
+ case 6: b |= ((u64)end[5]) << 40; fallthrough;
+ case 5: b |= ((u64)end[4]) << 32; fallthrough;
+ case 4: b |= get_unaligned_le32(end); break;
+ case 3: b |= ((u64)end[2]) << 16; fallthrough;
+ case 2: b |= get_unaligned_le16(end); break;
+ case 1: b |= end[0];
+ }
+#endif
+ POSTAMBLE
+}
+EXPORT_SYMBOL(__siphash_unaligned);
+
+/**
+ * siphash_1u64 - compute 64-bit siphash PRF value of a u64
+ * @first: first u64
+ * @key: the siphash key
+ */
+u64 siphash_1u64(const u64 first, const siphash_key_t *key)
+{
+ PREAMBLE(8)
+ v3 ^= first;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= first;
+ POSTAMBLE
+}
+EXPORT_SYMBOL(siphash_1u64);
+
+/**
+ * siphash_2u64 - compute 64-bit siphash PRF value of 2 u64
+ * @first: first u64
+ * @second: second u64
+ * @key: the siphash key
+ */
+u64 siphash_2u64(const u64 first, const u64 second, const siphash_key_t *key)
+{
+ PREAMBLE(16)
+ v3 ^= first;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= first;
+ v3 ^= second;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= second;
+ POSTAMBLE
+}
+EXPORT_SYMBOL(siphash_2u64);
+
+/**
+ * siphash_3u64 - compute 64-bit siphash PRF value of 3 u64
+ * @first: first u64
+ * @second: second u64
+ * @third: third u64
+ * @key: the siphash key
+ */
+u64 siphash_3u64(const u64 first, const u64 second, const u64 third,
+ const siphash_key_t *key)
+{
+ PREAMBLE(24)
+ v3 ^= first;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= first;
+ v3 ^= second;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= second;
+ v3 ^= third;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= third;
+ POSTAMBLE
+}
+EXPORT_SYMBOL(siphash_3u64);
+
+/**
+ * siphash_4u64 - compute 64-bit siphash PRF value of 4 u64
+ * @first: first u64
+ * @second: second u64
+ * @third: third u64
+ * @forth: forth u64
+ * @key: the siphash key
+ */
+u64 siphash_4u64(const u64 first, const u64 second, const u64 third,
+ const u64 forth, const siphash_key_t *key)
+{
+ PREAMBLE(32)
+ v3 ^= first;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= first;
+ v3 ^= second;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= second;
+ v3 ^= third;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= third;
+ v3 ^= forth;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= forth;
+ POSTAMBLE
+}
+EXPORT_SYMBOL(siphash_4u64);
+
+u64 siphash_1u32(const u32 first, const siphash_key_t *key)
+{
+ PREAMBLE(4)
+ b |= first;
+ POSTAMBLE
+}
+EXPORT_SYMBOL(siphash_1u32);
+
+u64 siphash_3u32(const u32 first, const u32 second, const u32 third,
+ const siphash_key_t *key)
+{
+ u64 combined = (u64)second << 32 | first;
+ PREAMBLE(12)
+ v3 ^= combined;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= combined;
+ b |= third;
+ POSTAMBLE
+}
+EXPORT_SYMBOL(siphash_3u32);
+
+#if BITS_PER_LONG == 64
+/* Note that on 64-bit, we make HalfSipHash1-3 actually be SipHash1-3, for
+ * performance reasons. On 32-bit, below, we actually implement HalfSipHash1-3.
+ */
+
+#define HSIPROUND SIPROUND
+#define HPREAMBLE(len) PREAMBLE(len)
+#define HPOSTAMBLE \
+ v3 ^= b; \
+ HSIPROUND; \
+ v0 ^= b; \
+ v2 ^= 0xff; \
+ HSIPROUND; \
+ HSIPROUND; \
+ HSIPROUND; \
+ return (v0 ^ v1) ^ (v2 ^ v3);
+
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+u32 __hsiphash_aligned(const void *_data, size_t len, const hsiphash_key_t *key)
+{
+ const u8 *data = _data;
+ const u8 *end = data + len - (len % sizeof(u64));
+ const u8 left = len & (sizeof(u64) - 1);
+ u64 m;
+ HPREAMBLE(len)
+ for (; data != end; data += sizeof(u64)) {
+ m = le64_to_cpup(data);
+ v3 ^= m;
+ HSIPROUND;
+ v0 ^= m;
+ }
+#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64
+ if (left)
+ b |= le64_to_cpu((__force __le64)(load_unaligned_zeropad(data) &
+ bytemask_from_count(left)));
+#else
+ switch (left) {
+ case 7: b |= ((u64)end[6]) << 48; fallthrough;
+ case 6: b |= ((u64)end[5]) << 40; fallthrough;
+ case 5: b |= ((u64)end[4]) << 32; fallthrough;
+ case 4: b |= le32_to_cpup(data); break;
+ case 3: b |= ((u64)end[2]) << 16; fallthrough;
+ case 2: b |= le16_to_cpup(data); break;
+ case 1: b |= end[0];
+ }
+#endif
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(__hsiphash_aligned);
+#endif
+
+u32 __hsiphash_unaligned(const void *_data, size_t len,
+ const hsiphash_key_t *key)
+{
+ const u8 *data = _data;
+ const u8 *end = data + len - (len % sizeof(u64));
+ const u8 left = len & (sizeof(u64) - 1);
+ u64 m;
+ HPREAMBLE(len)
+ for (; data != end; data += sizeof(u64)) {
+ m = get_unaligned_le64(data);
+ v3 ^= m;
+ HSIPROUND;
+ v0 ^= m;
+ }
+#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64
+ if (left)
+ b |= le64_to_cpu((__force __le64)(load_unaligned_zeropad(data) &
+ bytemask_from_count(left)));
+#else
+ switch (left) {
+ case 7: b |= ((u64)end[6]) << 48; fallthrough;
+ case 6: b |= ((u64)end[5]) << 40; fallthrough;
+ case 5: b |= ((u64)end[4]) << 32; fallthrough;
+ case 4: b |= get_unaligned_le32(end); break;
+ case 3: b |= ((u64)end[2]) << 16; fallthrough;
+ case 2: b |= get_unaligned_le16(end); break;
+ case 1: b |= end[0];
+ }
+#endif
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(__hsiphash_unaligned);
+
+/**
+ * hsiphash_1u32 - compute 64-bit hsiphash PRF value of a u32
+ * @first: first u32
+ * @key: the hsiphash key
+ */
+u32 hsiphash_1u32(const u32 first, const hsiphash_key_t *key)
+{
+ HPREAMBLE(4)
+ b |= first;
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(hsiphash_1u32);
+
+/**
+ * hsiphash_2u32 - compute 32-bit hsiphash PRF value of 2 u32
+ * @first: first u32
+ * @second: second u32
+ * @key: the hsiphash key
+ */
+u32 hsiphash_2u32(const u32 first, const u32 second, const hsiphash_key_t *key)
+{
+ u64 combined = (u64)second << 32 | first;
+ HPREAMBLE(8)
+ v3 ^= combined;
+ HSIPROUND;
+ v0 ^= combined;
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(hsiphash_2u32);
+
+/**
+ * hsiphash_3u32 - compute 32-bit hsiphash PRF value of 3 u32
+ * @first: first u32
+ * @second: second u32
+ * @third: third u32
+ * @key: the hsiphash key
+ */
+u32 hsiphash_3u32(const u32 first, const u32 second, const u32 third,
+ const hsiphash_key_t *key)
+{
+ u64 combined = (u64)second << 32 | first;
+ HPREAMBLE(12)
+ v3 ^= combined;
+ HSIPROUND;
+ v0 ^= combined;
+ b |= third;
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(hsiphash_3u32);
+
+/**
+ * hsiphash_4u32 - compute 32-bit hsiphash PRF value of 4 u32
+ * @first: first u32
+ * @second: second u32
+ * @third: third u32
+ * @forth: forth u32
+ * @key: the hsiphash key
+ */
+u32 hsiphash_4u32(const u32 first, const u32 second, const u32 third,
+ const u32 forth, const hsiphash_key_t *key)
+{
+ u64 combined = (u64)second << 32 | first;
+ HPREAMBLE(16)
+ v3 ^= combined;
+ HSIPROUND;
+ v0 ^= combined;
+ combined = (u64)forth << 32 | third;
+ v3 ^= combined;
+ HSIPROUND;
+ v0 ^= combined;
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(hsiphash_4u32);
+#else
+#define HSIPROUND HSIPHASH_PERMUTATION(v0, v1, v2, v3)
+
+#define HPREAMBLE(len) \
+ u32 v0 = HSIPHASH_CONST_0; \
+ u32 v1 = HSIPHASH_CONST_1; \
+ u32 v2 = HSIPHASH_CONST_2; \
+ u32 v3 = HSIPHASH_CONST_3; \
+ u32 b = ((u32)(len)) << 24; \
+ v3 ^= key->key[1]; \
+ v2 ^= key->key[0]; \
+ v1 ^= key->key[1]; \
+ v0 ^= key->key[0];
+
+#define HPOSTAMBLE \
+ v3 ^= b; \
+ HSIPROUND; \
+ v0 ^= b; \
+ v2 ^= 0xff; \
+ HSIPROUND; \
+ HSIPROUND; \
+ HSIPROUND; \
+ return v1 ^ v3;
+
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+u32 __hsiphash_aligned(const void *_data, size_t len, const hsiphash_key_t *key)
+{
+ const u8 *data = _data;
+ const u8 *end = data + len - (len % sizeof(u32));
+ const u8 left = len & (sizeof(u32) - 1);
+ u32 m;
+ HPREAMBLE(len)
+ for (; data != end; data += sizeof(u32)) {
+ m = le32_to_cpup(data);
+ v3 ^= m;
+ HSIPROUND;
+ v0 ^= m;
+ }
+ switch (left) {
+ case 3: b |= ((u32)end[2]) << 16; fallthrough;
+ case 2: b |= le16_to_cpup(data); break;
+ case 1: b |= end[0];
+ }
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(__hsiphash_aligned);
+#endif
+
+u32 __hsiphash_unaligned(const void *_data, size_t len,
+ const hsiphash_key_t *key)
+{
+ const u8 *data = _data;
+ const u8 *end = data + len - (len % sizeof(u32));
+ const u8 left = len & (sizeof(u32) - 1);
+ u32 m;
+ HPREAMBLE(len)
+ for (; data != end; data += sizeof(u32)) {
+ m = get_unaligned_le32(data);
+ v3 ^= m;
+ HSIPROUND;
+ v0 ^= m;
+ }
+ switch (left) {
+ case 3: b |= ((u32)end[2]) << 16; fallthrough;
+ case 2: b |= get_unaligned_le16(end); break;
+ case 1: b |= end[0];
+ }
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(__hsiphash_unaligned);
+
+/**
+ * hsiphash_1u32 - compute 32-bit hsiphash PRF value of a u32
+ * @first: first u32
+ * @key: the hsiphash key
+ */
+u32 hsiphash_1u32(const u32 first, const hsiphash_key_t *key)
+{
+ HPREAMBLE(4)
+ v3 ^= first;
+ HSIPROUND;
+ v0 ^= first;
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(hsiphash_1u32);
+
+/**
+ * hsiphash_2u32 - compute 32-bit hsiphash PRF value of 2 u32
+ * @first: first u32
+ * @second: second u32
+ * @key: the hsiphash key
+ */
+u32 hsiphash_2u32(const u32 first, const u32 second, const hsiphash_key_t *key)
+{
+ HPREAMBLE(8)
+ v3 ^= first;
+ HSIPROUND;
+ v0 ^= first;
+ v3 ^= second;
+ HSIPROUND;
+ v0 ^= second;
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(hsiphash_2u32);
+
+/**
+ * hsiphash_3u32 - compute 32-bit hsiphash PRF value of 3 u32
+ * @first: first u32
+ * @second: second u32
+ * @third: third u32
+ * @key: the hsiphash key
+ */
+u32 hsiphash_3u32(const u32 first, const u32 second, const u32 third,
+ const hsiphash_key_t *key)
+{
+ HPREAMBLE(12)
+ v3 ^= first;
+ HSIPROUND;
+ v0 ^= first;
+ v3 ^= second;
+ HSIPROUND;
+ v0 ^= second;
+ v3 ^= third;
+ HSIPROUND;
+ v0 ^= third;
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(hsiphash_3u32);
+
+/**
+ * hsiphash_4u32 - compute 32-bit hsiphash PRF value of 4 u32
+ * @first: first u32
+ * @second: second u32
+ * @third: third u32
+ * @forth: forth u32
+ * @key: the hsiphash key
+ */
+u32 hsiphash_4u32(const u32 first, const u32 second, const u32 third,
+ const u32 forth, const hsiphash_key_t *key)
+{
+ HPREAMBLE(16)
+ v3 ^= first;
+ HSIPROUND;
+ v0 ^= first;
+ v3 ^= second;
+ HSIPROUND;
+ v0 ^= second;
+ v3 ^= third;
+ HSIPROUND;
+ v0 ^= third;
+ v3 ^= forth;
+ HSIPROUND;
+ v0 ^= forth;
+ HPOSTAMBLE
+}
+EXPORT_SYMBOL(hsiphash_4u32);
+#endif
diff --git a/sys/compat/linuxkpi/common/src/linux_xarray.c b/sys/compat/linuxkpi/common/src/linux_xarray.c
index 3f07f6d7c59f..8caefbaf7e50 100644
--- a/sys/compat/linuxkpi/common/src/linux_xarray.c
+++ b/sys/compat/linuxkpi/common/src/linux_xarray.c
@@ -389,7 +389,7 @@ __xa_empty(struct xarray *xa)
XA_ASSERT_LOCKED(xa);
- return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp));
+ return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp, 0));
}
bool
@@ -426,7 +426,7 @@ __xa_next(struct xarray *xa, unsigned long *pindex, bool not_first)
return (NULL);
}
- found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot);
+ found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot, 0);
if (likely(found)) {
retval = *ppslot;
if (retval == NULL_VALUE)
diff --git a/sys/compat/linuxkpi/dummy/include/linux/eventfd.h b/sys/compat/linuxkpi/dummy/include/linux/fpu.h
index e69de29bb2d1..e69de29bb2d1 100644
--- a/sys/compat/linuxkpi/dummy/include/linux/eventfd.h
+++ b/sys/compat/linuxkpi/dummy/include/linux/fpu.h
diff --git a/sys/compat/linuxkpi/dummy/include/media/cec-notifier.h b/sys/compat/linuxkpi/dummy/include/linux/kdebug.h
index e69de29bb2d1..e69de29bb2d1 100644
--- a/sys/compat/linuxkpi/dummy/include/media/cec-notifier.h
+++ b/sys/compat/linuxkpi/dummy/include/linux/kdebug.h
diff --git a/sys/compat/linuxkpi/dummy/include/net/page_pool/helpers.h b/sys/compat/linuxkpi/dummy/include/net/page_pool/helpers.h
deleted file mode 100644
index e69de29bb2d1..000000000000
--- a/sys/compat/linuxkpi/dummy/include/net/page_pool/helpers.h
+++ /dev/null