From 1e3db1de0cf6b904373e0f4c9e738b9713f3e17b Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Tue, 21 Feb 2017 12:43:02 +0000 Subject: [PATCH] Make the LinuxKPI task struct persistent accross system calls. A set of helper functions have been added to manage the life of the LinuxKPI task struct. When an external system call or task is invoked, a check is made to create the task struct by demand. A thread destructor callback is registered to free the task struct when a thread exits to avoid memory leaks. This change lays the ground for emulating the Linux kernel more closely which is a dependency by the code using the LinuxKPI APIs. Add new dedicated td_lkpi_task field has been added to struct thread instead of abusing td_retval[1]. Fix some header file inclusions to make LINT kernel build properly after this change. Bump the __FreeBSD_version to force a rebuild of all kernel modules. MFC after: 1 week Sponsored by: Mellanox Technologies --- .../linuxkpi/common/include/linux/bitops.h | 1 + .../linuxkpi/common/include/linux/compat.h | 25 +++- .../linuxkpi/common/include/linux/file.h | 1 + .../linuxkpi/common/include/linux/jiffies.h | 1 - .../linuxkpi/common/include/linux/kdev_t.h | 2 + .../linuxkpi/common/include/linux/kernel.h | 2 +- .../linuxkpi/common/include/linux/kthread.h | 81 +++--------- .../linuxkpi/common/include/linux/rwlock.h | 1 + .../linuxkpi/common/include/linux/rwsem.h | 1 + .../linuxkpi/common/include/linux/sched.h | 45 +++---- .../linuxkpi/common/include/linux/semaphore.h | 1 + .../linuxkpi/common/include/linux/spinlock.h | 2 +- .../linuxkpi/common/include/linux/types.h | 2 + .../linuxkpi/common/include/linux/wait.h | 2 - sys/compat/linuxkpi/common/src/linux_compat.c | 69 +++------- .../linuxkpi/common/src/linux_current.c | 94 ++++++++++++++ .../linuxkpi/common/src/linux_kthread.c | 120 ++++++++++++++++++ sys/compat/linuxkpi/common/src/linux_pci.c | 31 +---- sys/conf/files | 4 + sys/modules/linuxkpi/Makefile | 2 + sys/sys/param.h | 2 +- sys/sys/proc.h | 1 + 22 files changed, 312 insertions(+), 178 deletions(-) create mode 100644 sys/compat/linuxkpi/common/src/linux_current.c create mode 100644 sys/compat/linuxkpi/common/src/linux_kthread.c diff --git a/sys/compat/linuxkpi/common/include/linux/bitops.h b/sys/compat/linuxkpi/common/include/linux/bitops.h index 9e1fa2bc456..2c521318804 100644 --- a/sys/compat/linuxkpi/common/include/linux/bitops.h +++ b/sys/compat/linuxkpi/common/include/linux/bitops.h @@ -31,6 +31,7 @@ #ifndef _LINUX_BITOPS_H_ #define _LINUX_BITOPS_H_ +#include #include #include #include diff --git a/sys/compat/linuxkpi/common/include/linux/compat.h b/sys/compat/linuxkpi/common/include/linux/compat.h index 01b8a85b976..62ea3363394 100644 --- a/sys/compat/linuxkpi/common/include/linux/compat.h +++ b/sys/compat/linuxkpi/common/include/linux/compat.h @@ -2,7 +2,7 @@ * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. - * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. + * Copyright (c) 2013-2017 Mellanox Technologies, Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,10 +31,29 @@ #ifndef _LINUX_COMPAT_H_ #define _LINUX_COMPAT_H_ +#include +#include +#include + struct thread; struct task_struct; -void linux_set_current(struct thread *td, struct task_struct *t); -void linux_clear_current(struct thread *td); +extern int linux_alloc_current(struct thread *, int flags); +extern void linux_free_current(struct task_struct *); + +static inline void +linux_set_current(struct thread *td) +{ + if (__predict_false(td->td_lkpi_task == NULL)) + linux_alloc_current(td, M_WAITOK); +} + +static inline int +linux_set_current_flags(struct thread *td, int flags) +{ + if (__predict_false(td->td_lkpi_task == NULL)) + return (linux_alloc_current(td, flags)); + return (0); +} #endif /* _LINUX_COMPAT_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/file.h b/sys/compat/linuxkpi/common/include/linux/file.h index 559ac043768..0661e70a569 100644 --- a/sys/compat/linuxkpi/common/include/linux/file.h +++ b/sys/compat/linuxkpi/common/include/linux/file.h @@ -39,6 +39,7 @@ #include #include +#include struct linux_file; diff --git a/sys/compat/linuxkpi/common/include/linux/jiffies.h b/sys/compat/linuxkpi/common/include/linux/jiffies.h index 9a85f616152..a95e6064f41 100644 --- a/sys/compat/linuxkpi/common/include/linux/jiffies.h +++ b/sys/compat/linuxkpi/common/include/linux/jiffies.h @@ -32,7 +32,6 @@ #define _LINUX_JIFFIES_H_ #include -#include #include #include diff --git a/sys/compat/linuxkpi/common/include/linux/kdev_t.h b/sys/compat/linuxkpi/common/include/linux/kdev_t.h index c0bb97e5ba4..447d7af2f84 100644 --- a/sys/compat/linuxkpi/common/include/linux/kdev_t.h +++ b/sys/compat/linuxkpi/common/include/linux/kdev_t.h @@ -31,6 +31,8 @@ #ifndef _LINUX_KDEV_T_H_ #define _LINUX_KDEV_T_H_ +#include + #define MAJOR(dev) major((dev)) #define MINOR(dev) minor((dev)) #define MKDEV(ma, mi) makedev((ma), (mi)) diff --git a/sys/compat/linuxkpi/common/include/linux/kernel.h b/sys/compat/linuxkpi/common/include/linux/kernel.h index ead6a8a72bf..fabdba5df51 100644 --- a/sys/compat/linuxkpi/common/include/linux/kernel.h +++ b/sys/compat/linuxkpi/common/include/linux/kernel.h @@ -45,7 +45,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/sys/compat/linuxkpi/common/include/linux/kthread.h b/sys/compat/linuxkpi/common/include/linux/kthread.h index 2e0da123d52..eaf2b31a71a 100644 --- a/sys/compat/linuxkpi/common/include/linux/kthread.h +++ b/sys/compat/linuxkpi/common/include/linux/kthread.h @@ -2,7 +2,7 @@ * 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) 2013-2017 Mellanox Technologies, Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,74 +31,27 @@ #ifndef _LINUX_KTHREAD_H_ #define _LINUX_KTHREAD_H_ -#include -#include -#include -#include -#include -#include - -#include #include -static inline void -linux_kthread_fn(void *arg) -{ - struct task_struct *task; - struct thread *td = curthread; - - task = arg; - task_struct_fill(td, task); - task_struct_set(td, task); - if (task->should_stop == 0) - task->task_ret = task->task_fn(task->task_data); - PROC_LOCK(td->td_proc); - task->should_stop = TASK_STOPPED; - wakeup(task); - PROC_UNLOCK(td->td_proc); - task_struct_set(td, NULL); - kthread_exit(); -} - -static inline struct task_struct * -linux_kthread_create(int (*threadfn)(void *data), void *data) -{ - struct task_struct *task; - - task = kzalloc(sizeof(*task), GFP_KERNEL); - task->task_fn = threadfn; - task->task_data = data; - - return (task); -} +#include +#include -#define kthread_run(fn, data, fmt, ...) \ -({ \ - struct task_struct *_task; \ +#define kthread_run(fn, data, fmt, ...) ({ \ + struct task_struct *__task; \ + struct thread *__td; \ \ - _task = linux_kthread_create((fn), (data)); \ - if (kthread_add(linux_kthread_fn, _task, NULL, &_task->task_thread, \ - 0, 0, fmt, ## __VA_ARGS__)) { \ - kfree(_task); \ - _task = NULL; \ - } \ - _task; \ + if (kthread_add(linux_kthread_fn, NULL, NULL, &__td, \ + RFSTOPPED, 0, fmt, ## __VA_ARGS__)) \ + __task = NULL; \ + else \ + __task = linux_kthread_setup_and_run(__td, fn, data); \ + __task; \ }) -#define kthread_should_stop() current->should_stop - -static inline int -kthread_stop(struct task_struct *task) -{ - - PROC_LOCK(task->task_thread->td_proc); - task->should_stop = TASK_SHOULD_STOP; - wake_up_process(task); - while (task->should_stop != TASK_STOPPED) - msleep(task, &task->task_thread->td_proc->p_mtx, PWAIT, - "kstop", hz); - PROC_UNLOCK(task->task_thread->td_proc); - return task->task_ret; -} +extern int kthread_stop(struct task_struct *); +extern bool kthread_should_stop_task(struct task_struct *); +extern bool kthread_should_stop(void); +extern void linux_kthread_fn(void *); +extern struct task_struct *linux_kthread_setup_and_run(struct thread *, linux_task_fn_t *, void *arg); #endif /* _LINUX_KTHREAD_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/rwlock.h b/sys/compat/linuxkpi/common/include/linux/rwlock.h index 54c53dc9498..4c9529e843e 100644 --- a/sys/compat/linuxkpi/common/include/linux/rwlock.h +++ b/sys/compat/linuxkpi/common/include/linux/rwlock.h @@ -34,6 +34,7 @@ #include #include #include +#include typedef struct { struct rwlock rw; diff --git a/sys/compat/linuxkpi/common/include/linux/rwsem.h b/sys/compat/linuxkpi/common/include/linux/rwsem.h index 22ad4dc62a9..7ca066125a4 100644 --- a/sys/compat/linuxkpi/common/include/linux/rwsem.h +++ b/sys/compat/linuxkpi/common/include/linux/rwsem.h @@ -34,6 +34,7 @@ #include #include #include +#include struct rw_semaphore { struct sx sx; diff --git a/sys/compat/linuxkpi/common/include/linux/sched.h b/sys/compat/linuxkpi/common/include/linux/sched.h index c9f2a399904..04abc823077 100644 --- a/sys/compat/linuxkpi/common/include/linux/sched.h +++ b/sys/compat/linuxkpi/common/include/linux/sched.h @@ -2,7 +2,7 @@ * 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) 2013-2017 Mellanox Technologies, Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,6 +37,12 @@ #include #include +#include +#include +#include + +#include + #define MAX_SCHEDULE_TIMEOUT LONG_MAX #define TASK_RUNNING 0 @@ -46,41 +52,22 @@ #define TASK_WAKEKILL 128 #define TASK_WAKING 256 -#define TASK_SHOULD_STOP 1 -#define TASK_STOPPED 2 - -/* - * A task_struct is only provided for threads created by kthread() and - * file operation callbacks. - * - * Using these routines outside the above mentioned contexts will - * cause panics because no task_struct is assigned and td_retval[1] is - * overwritten by syscalls. - */ struct task_struct { - struct thread *task_thread; - int (*task_fn)(void *data); - void *task_data; + struct thread *task_thread; + linux_task_fn_t *task_fn; + void *task_data; int task_ret; int state; - int should_stop; + atomic_t kthread_flags; pid_t pid; const char *comm; - void *bsd_ioctl_data; - unsigned bsd_ioctl_len; + void *bsd_ioctl_data; + unsigned bsd_ioctl_len; + struct completion parked; + struct completion exited; }; -#define current task_struct_get(curthread) -#define task_struct_get(x) ((struct task_struct *)(uintptr_t)(x)->td_retval[1]) -#define task_struct_fill(x, y) do { \ - (y)->task_thread = (x); \ - (y)->comm = (x)->td_name; \ - (y)->pid = (x)->td_tid; \ -} while (0) -#define task_struct_set(x, y) (x)->td_retval[1] = (uintptr_t)(y) - -/* ensure the task_struct pointer fits into the td_retval[1] field */ -CTASSERT(sizeof(((struct thread *)0)->td_retval[1]) >= sizeof(uintptr_t)); +#define current ((struct task_struct *)curthread->td_lkpi_task) #define set_current_state(x) \ atomic_store_rel_int((volatile int *)¤t->state, (x)) diff --git a/sys/compat/linuxkpi/common/include/linux/semaphore.h b/sys/compat/linuxkpi/common/include/linux/semaphore.h index 022a0164840..59a35311a5c 100644 --- a/sys/compat/linuxkpi/common/include/linux/semaphore.h +++ b/sys/compat/linuxkpi/common/include/linux/semaphore.h @@ -34,6 +34,7 @@ #include #include #include +#include /* * XXX BSD semaphores are disused and slow. They also do not provide a diff --git a/sys/compat/linuxkpi/common/include/linux/spinlock.h b/sys/compat/linuxkpi/common/include/linux/spinlock.h index 97c83e0ed03..4beb6fe45f2 100644 --- a/sys/compat/linuxkpi/common/include/linux/spinlock.h +++ b/sys/compat/linuxkpi/common/include/linux/spinlock.h @@ -35,9 +35,9 @@ #include #include #include +#include #include -#include #include typedef struct { diff --git a/sys/compat/linuxkpi/common/include/linux/types.h b/sys/compat/linuxkpi/common/include/linux/types.h index c9c37284a70..28abc9ec269 100644 --- a/sys/compat/linuxkpi/common/include/linux/types.h +++ b/sys/compat/linuxkpi/common/include/linux/types.h @@ -63,4 +63,6 @@ typedef u64 phys_addr_t; #define DECLARE_BITMAP(n, bits) \ unsigned long n[howmany(bits, sizeof(long) * 8)] +typedef int linux_task_fn_t(void *data); + #endif /* _LINUX_TYPES_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/wait.h b/sys/compat/linuxkpi/common/include/linux/wait.h index 7ae6464c6d4..14da6d264ce 100644 --- a/sys/compat/linuxkpi/common/include/linux/wait.h +++ b/sys/compat/linuxkpi/common/include/linux/wait.h @@ -32,8 +32,6 @@ #define _LINUX_WAIT_H_ #include -#include -#include #include #include diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c index 54bd33bc7ad..2b94a03e7e9 100644 --- a/sys/compat/linuxkpi/common/src/linux_compat.c +++ b/sys/compat/linuxkpi/common/src/linux_compat.c @@ -384,32 +384,14 @@ kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype, return kobject_add_complete(kobj, parent); } -void -linux_set_current(struct thread *td, struct task_struct *t) -{ - memset(t, 0, sizeof(*t)); - task_struct_fill(td, t); - task_struct_set(td, t); -} - -void -linux_clear_current(struct thread *td) -{ - task_struct_set(td, NULL); -} - static void linux_file_dtor(void *cdp) { struct linux_file *filp; - struct task_struct t; - struct thread *td; - td = curthread; + linux_set_current(curthread); filp = cdp; - linux_set_current(td, &t); filp->f_op->release(filp->f_vnode, filp); - linux_clear_current(td); vdrop(filp->f_vnode); kfree(filp); } @@ -419,7 +401,6 @@ linux_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct linux_cdev *ldev; struct linux_file *filp; - struct task_struct t; struct file *file; int error; @@ -433,7 +414,7 @@ linux_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) filp->f_flags = file->f_flag; vhold(file->f_vnode); filp->f_vnode = file->f_vnode; - linux_set_current(td, &t); + linux_set_current(td); if (filp->f_op->open) { error = -filp->f_op->open(file->f_vnode, filp); if (error) { @@ -447,7 +428,6 @@ linux_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) kfree(filp); } done: - linux_clear_current(td); return (error); } @@ -538,7 +518,6 @@ linux_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, { struct linux_cdev *ldev; struct linux_file *filp; - struct task_struct t; struct file *file; unsigned size; int error; @@ -550,7 +529,8 @@ linux_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; - linux_set_current(td, &t); + + linux_set_current(td); size = IOCPARM_LEN(cmd); /* refer to logic in sys_ioctl() */ if (size > 0) { @@ -560,8 +540,8 @@ linux_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, * Background: Linux code expects a user-space address * while FreeBSD supplies a kernel-space address. */ - t.bsd_ioctl_data = data; - t.bsd_ioctl_len = size; + current->bsd_ioctl_data = data; + current->bsd_ioctl_len = size; data = (void *)LINUX_IOCTL_MIN_PTR; } else { /* fetch user-space pointer */ @@ -571,7 +551,10 @@ linux_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, error = -filp->f_op->unlocked_ioctl(filp, cmd, (u_long)data); else error = ENOTTY; - linux_clear_current(td); + if (size > 0) { + current->bsd_ioctl_data = NULL; + current->bsd_ioctl_len = 0; + } return (error); } @@ -581,7 +564,6 @@ linux_dev_read(struct cdev *dev, struct uio *uio, int ioflag) { struct linux_cdev *ldev; struct linux_file *filp; - struct task_struct t; struct thread *td; struct file *file; ssize_t bytes; @@ -598,7 +580,7 @@ linux_dev_read(struct cdev *dev, struct uio *uio, int ioflag) /* XXX no support for I/O vectors currently */ if (uio->uio_iovcnt != 1) return (EOPNOTSUPP); - linux_set_current(td, &t); + linux_set_current(td); if (filp->f_op->read) { bytes = filp->f_op->read(filp, uio->uio_iov->iov_base, uio->uio_iov->iov_len, &uio->uio_offset); @@ -611,7 +593,6 @@ linux_dev_read(struct cdev *dev, struct uio *uio, int ioflag) error = -bytes; } else error = ENXIO; - linux_clear_current(td); return (error); } @@ -621,7 +602,6 @@ linux_dev_write(struct cdev *dev, struct uio *uio, int ioflag) { struct linux_cdev *ldev; struct linux_file *filp; - struct task_struct t; struct thread *td; struct file *file; ssize_t bytes; @@ -638,7 +618,7 @@ linux_dev_write(struct cdev *dev, struct uio *uio, int ioflag) /* XXX no support for I/O vectors currently */ if (uio->uio_iovcnt != 1) return (EOPNOTSUPP); - linux_set_current(td, &t); + linux_set_current(td); if (filp->f_op->write) { bytes = filp->f_op->write(filp, uio->uio_iov->iov_base, uio->uio_iov->iov_len, &uio->uio_offset); @@ -651,7 +631,6 @@ linux_dev_write(struct cdev *dev, struct uio *uio, int ioflag) error = -bytes; } else error = ENXIO; - linux_clear_current(td); return (error); } @@ -661,7 +640,6 @@ linux_dev_poll(struct cdev *dev, int events, struct thread *td) { struct linux_cdev *ldev; struct linux_file *filp; - struct task_struct t; struct file *file; int revents; int error; @@ -673,12 +651,11 @@ linux_dev_poll(struct cdev *dev, int events, struct thread *td) if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; - linux_set_current(td, &t); + linux_set_current(td); if (filp->f_op->poll) revents = filp->f_op->poll(filp, NULL) & events; else revents = 0; - linux_clear_current(td); return (revents); } @@ -690,7 +667,6 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *offset, struct linux_cdev *ldev; struct linux_file *filp; struct thread *td; - struct task_struct t; struct file *file; struct vm_area_struct vma; int error; @@ -703,7 +679,7 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *offset, if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; - linux_set_current(td, &t); + linux_set_current(td); vma.vm_start = 0; vma.vm_end = size; vma.vm_pgoff = *offset / PAGE_SIZE; @@ -735,7 +711,6 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *offset, } else error = ENODEV; done: - linux_clear_current(td); return (error); } @@ -756,7 +731,6 @@ linux_file_read(struct file *file, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { struct linux_file *filp; - struct task_struct t; ssize_t bytes; int error; @@ -766,7 +740,7 @@ linux_file_read(struct file *file, struct uio *uio, struct ucred *active_cred, /* XXX no support for I/O vectors currently */ if (uio->uio_iovcnt != 1) return (EOPNOTSUPP); - linux_set_current(td, &t); + linux_set_current(td); if (filp->f_op->read) { bytes = filp->f_op->read(filp, uio->uio_iov->iov_base, uio->uio_iov->iov_len, &uio->uio_offset); @@ -779,7 +753,6 @@ linux_file_read(struct file *file, struct uio *uio, struct ucred *active_cred, error = -bytes; } else error = ENXIO; - linux_clear_current(td); return (error); } @@ -789,17 +762,15 @@ linux_file_poll(struct file *file, int events, struct ucred *active_cred, struct thread *td) { struct linux_file *filp; - struct task_struct t; int revents; filp = (struct linux_file *)file->f_data; filp->f_flags = file->f_flag; - linux_set_current(td, &t); + linux_set_current(td); if (filp->f_op->poll) revents = filp->f_op->poll(filp, NULL) & events; else revents = 0; - linux_clear_current(td); return (revents); } @@ -808,14 +779,12 @@ static int linux_file_close(struct file *file, struct thread *td) { struct linux_file *filp; - struct task_struct t; int error; filp = (struct linux_file *)file->f_data; filp->f_flags = file->f_flag; - linux_set_current(td, &t); + linux_set_current(td); error = -filp->f_op->release(NULL, filp); - linux_clear_current(td); funsetown(&filp->f_sigio); kfree(filp); @@ -827,14 +796,13 @@ linux_file_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *cred, struct thread *td) { struct linux_file *filp; - struct task_struct t; int error; filp = (struct linux_file *)fp->f_data; filp->f_flags = fp->f_flag; error = 0; - linux_set_current(td, &t); + linux_set_current(td); switch (cmd) { case FIONBIO: break; @@ -856,7 +824,6 @@ linux_file_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *cred, error = ENOTTY; break; } - linux_clear_current(td); return (error); } diff --git a/sys/compat/linuxkpi/common/src/linux_current.c b/sys/compat/linuxkpi/common/src/linux_current.c new file mode 100644 index 00000000000..6b4442cb4bc --- /dev/null +++ b/sys/compat/linuxkpi/common/src/linux_current.c @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 2017 Hans Petter Selasky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include + +static eventhandler_tag linuxkpi_thread_dtor_tag; + +static MALLOC_DEFINE(M_LINUX_CURRENT, "linuxcurrent", "LinuxKPI task structure"); + +int +linux_alloc_current(struct thread *td, int flags) +{ + struct task_struct *ts; + + MPASS(td->td_lkpi_task == NULL); + + ts = malloc(sizeof(*ts), M_LINUX_CURRENT, flags | M_ZERO); + if (ts == NULL) + return (ENOMEM); + + atomic_set(&ts->kthread_flags, 0); + ts->task_thread = td; + ts->comm = td->td_name; + ts->pid = td->td_tid; + ts->state = TASK_RUNNING; + td->td_lkpi_task = ts; + return (0); +} + +void +linux_free_current(struct task_struct *ts) +{ + free(ts, M_LINUX_CURRENT); +} + +static void +linuxkpi_thread_dtor(void *arg __unused, struct thread *td) +{ + struct task_struct *ts; + + ts = td->td_lkpi_task; + if (ts == NULL) + return; + + td->td_lkpi_task = NULL; + free(ts, M_LINUX_CURRENT); +} + +static void +linux_current_init(void *arg __unused) +{ + linuxkpi_thread_dtor_tag = EVENTHANDLER_REGISTER(thread_dtor, + linuxkpi_thread_dtor, NULL, EVENTHANDLER_PRI_ANY); +} +SYSINIT(linux_current, SI_SUB_EVENTHANDLER, SI_ORDER_SECOND, linux_current_init, NULL); + +static void +linux_current_uninit(void *arg __unused) +{ + EVENTHANDLER_DEREGISTER(thread_dtor, linuxkpi_thread_dtor_tag); +} +SYSUNINIT(linux_current, SI_SUB_EVENTHANDLER, SI_ORDER_SECOND, linux_current_uninit, NULL); diff --git a/sys/compat/linuxkpi/common/src/linux_kthread.c b/sys/compat/linuxkpi/common/src/linux_kthread.c new file mode 100644 index 00000000000..90da31f1ad0 --- /dev/null +++ b/sys/compat/linuxkpi/common/src/linux_kthread.c @@ -0,0 +1,120 @@ +/*- + * Copyright (c) 2017 Hans Petter Selasky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include + +enum { + KTHREAD_SHOULD_STOP_MASK = (1 << 0), + KTHREAD_SHOULD_PARK_MASK = (1 << 1), + KTHREAD_IS_PARKED_MASK = (1 << 2), +}; + +bool +kthread_should_stop_task(struct task_struct *task) +{ + + return (atomic_read(&task->kthread_flags) & KTHREAD_SHOULD_STOP_MASK); +} + +bool +kthread_should_stop(void) +{ + + return (atomic_read(¤t->kthread_flags) & KTHREAD_SHOULD_STOP_MASK); +} + +int +kthread_stop(struct task_struct *task) +{ + int retval; + + /* + * Assume task is still alive else caller should not call + * kthread_stop(): + */ + atomic_or(KTHREAD_SHOULD_STOP_MASK, &task->kthread_flags); + wake_up_process(task); + wait_for_completion(&task->exited); + + /* + * Get return code and free task structure: + */ + retval = task->task_ret; + linux_free_current(task); + + return (retval); +} + +struct task_struct * +linux_kthread_setup_and_run(struct thread *td, linux_task_fn_t *task_fn, void *arg) +{ + struct task_struct *task; + + linux_set_current(td); + + task = td->td_lkpi_task; + task->task_fn = task_fn; + task->task_data = arg; + + thread_lock(td); + /* make sure the scheduler priority is raised */ + sched_prio(td, PI_SWI(SWI_NET)); + /* put thread into run-queue */ + sched_add(td, SRQ_BORING); + thread_unlock(td); + + return (task); +} + +void +linux_kthread_fn(void *arg __unused) +{ + struct task_struct *task = current; + + if (kthread_should_stop_task(task) == 0) + task->task_ret = task->task_fn(task->task_data); + + if (kthread_should_stop_task(task) != 0) { + struct thread *td = curthread; + + /* let kthread_stop() free data */ + td->td_lkpi_task = NULL; + + /* wakeup kthread_stop() */ + complete(&task->exited); + } + kthread_exit(); +} + diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c index 22db26495e5..84a38d87556 100644 --- a/sys/compat/linuxkpi/common/src/linux_pci.c +++ b/sys/compat/linuxkpi/common/src/linux_pci.c @@ -121,12 +121,9 @@ linux_pci_attach(device_t dev) struct pci_dev *pdev; struct pci_driver *pdrv; const struct pci_device_id *id; - struct task_struct t; - struct thread *td; int error; - td = curthread; - linux_set_current(td, &t); + linux_set_current(curthread); pdrv = linux_pci_find(dev, &id); pdev = device_get_softc(dev); pdev->dev.parent = &linux_root_device; @@ -159,7 +156,6 @@ linux_pci_attach(device_t dev) put_device(&pdev->dev); error = -error; } - linux_clear_current(td); return (error); } @@ -167,11 +163,8 @@ static int linux_pci_detach(device_t dev) { struct pci_dev *pdev; - struct task_struct t; - struct thread *td; - td = curthread; - linux_set_current(td, &t); + linux_set_current(curthread); pdev = device_get_softc(dev); DROP_GIANT(); pdev->pdrv->remove(pdev); @@ -180,7 +173,6 @@ linux_pci_detach(device_t dev) list_del(&pdev->links); spin_unlock(&pci_lock); put_device(&pdev->dev); - linux_clear_current(td); return (0); } @@ -190,18 +182,14 @@ linux_pci_suspend(device_t dev) { struct pm_message pm = { }; struct pci_dev *pdev; - struct task_struct t; - struct thread *td; int err; - td = curthread; - linux_set_current(td, &t); + linux_set_current(curthread); pdev = device_get_softc(dev); if (pdev->pdrv->suspend != NULL) err = -pdev->pdrv->suspend(pdev, pm); else err = 0; - linux_clear_current(td); return (err); } @@ -209,18 +197,14 @@ static int linux_pci_resume(device_t dev) { struct pci_dev *pdev; - struct task_struct t; - struct thread *td; int err; - td = curthread; - linux_set_current(td, &t); + linux_set_current(curthread); pdev = device_get_softc(dev); if (pdev->pdrv->resume != NULL) err = -pdev->pdrv->resume(pdev); else err = 0; - linux_clear_current(td); return (err); } @@ -228,18 +212,14 @@ static int linux_pci_shutdown(device_t dev) { struct pci_dev *pdev; - struct task_struct t; - struct thread *td; - td = curthread; - linux_set_current(td, &t); + linux_set_current(curthread); pdev = device_get_softc(dev); if (pdev->pdrv->shutdown != NULL) { DROP_GIANT(); pdev->pdrv->shutdown(pdev); PICKUP_GIANT(); } - linux_clear_current(td); return (0); } @@ -251,6 +231,7 @@ pci_register_driver(struct pci_driver *pdrv) bus = devclass_find("pci"); + linux_set_current(curthread); spin_lock(&pci_lock); list_add(&pdrv->links, &pci_drivers); spin_unlock(&pci_lock); diff --git a/sys/conf/files b/sys/conf/files index 9739c4d8f20..59e1592861d 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -4266,6 +4266,10 @@ compat/linuxkpi/common/src/linux_kmod.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_compat.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" +compat/linuxkpi/common/src/linux_current.c optional compat_linuxkpi \ + compile-with "${LINUXKPI_C}" +compat/linuxkpi/common/src/linux_kthread.c optional compat_linuxkpi \ + compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_pci.c optional compat_linuxkpi pci \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_idr.c optional compat_linuxkpi \ diff --git a/sys/modules/linuxkpi/Makefile b/sys/modules/linuxkpi/Makefile index bcfb03a6ae3..185fd400890 100644 --- a/sys/modules/linuxkpi/Makefile +++ b/sys/modules/linuxkpi/Makefile @@ -4,6 +4,8 @@ KMOD= linuxkpi SRCS= linux_kmod.c \ linux_compat.c \ + linux_current.c \ + linux_kthread.c \ linux_pci.c \ linux_radix.c \ linux_idr.c \ diff --git a/sys/sys/param.h b/sys/sys/param.h index ada0639342f..bb6f0b1f70c 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1200021 /* Master, propagated to newvers */ +#define __FreeBSD_version 1200022 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, diff --git a/sys/sys/proc.h b/sys/sys/proc.h index cc744a560e0..da2a87a2ca3 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -339,6 +339,7 @@ struct thread { void *td_emuldata; /* Emulator state data */ int td_lastcpu; /* (t) Last cpu we were on. */ int td_oncpu; /* (t) Which cpu we are on. */ + void *td_lkpi_task; /* LinuxKPI task struct pointer */ }; struct thread0_storage { -- 2.45.2