Index: sys/compat/linuxkpi/common/include/linux/fs.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/fs.h +++ sys/compat/linuxkpi/common/include/linux/fs.h @@ -323,4 +323,13 @@ #define shmem_truncate_range(...) \ linux_shmem_truncate_range(__VA_ARGS__) +static inline int +simple_open(struct inode *inode, struct file *file) +{ + + if (inode->i_private) + file->private_data = inode->i_private; + return (0); +} + #endif /* _LINUX_FS_H_ */ Index: sys/compat/linuxkpi/common/include/linux/interrupt.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/interrupt.h +++ sys/compat/linuxkpi/common/include/linux/interrupt.h @@ -189,11 +189,14 @@ struct tasklet_struct { TAILQ_ENTRY(tasklet_struct) entry; tasklet_func_t *func; + /* Our state implementation is different. Avoid same name as Linux. */ + volatile u_int _state; + atomic_t count; unsigned long data; }; #define DECLARE_TASKLET(name, func, data) \ -struct tasklet_struct name = { { NULL, NULL }, func, data } +struct tasklet_struct name = { { NULL, NULL }, func, ATOMIC_INIT(0), data } #define tasklet_hi_schedule(t) tasklet_schedule(t) @@ -202,6 +205,10 @@ extern void tasklet_init(struct tasklet_struct *, tasklet_func_t *, unsigned long data); extern void tasklet_enable(struct tasklet_struct *); +extern bool tasklet_is_enabled(struct tasklet_struct *); extern void tasklet_disable(struct tasklet_struct *); +extern int tasklet_trylock(struct tasklet_struct *); +extern void tasklet_unlock(struct tasklet_struct *); +extern void tasklet_unlock_wait(struct tasklet_struct *ts); #endif /* _LINUX_INTERRUPT_H_ */ Index: sys/compat/linuxkpi/common/include/linux/preempt.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/preempt.h +++ sys/compat/linuxkpi/common/include/linux/preempt.h @@ -34,6 +34,9 @@ #define in_interrupt() \ (curthread->td_intr_nesting_level || curthread->td_critnest) +#define in_task() \ + (curthread->td_intr_nesting_level == 0 && curthread->td_critnest == 0) + #define preempt_disable() critical_enter() #define preempt_enable() critical_exit() Index: sys/compat/linuxkpi/common/include/linux/sched.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/sched.h +++ sys/compat/linuxkpi/common/include/linux/sched.h @@ -80,6 +80,7 @@ int rcu_recurse; int bsd_interrupt_value; struct work_struct *work; /* current work struct, if set */ + struct task_struct *group_leader; }; #define current ({ \ Index: sys/compat/linuxkpi/common/include/linux/uaccess.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/uaccess.h +++ sys/compat/linuxkpi/common/include/linux/uaccess.h @@ -60,12 +60,17 @@ #define get_user(_x, _p) linux_copyin((_p), &(_x), sizeof(*(_p))) #define put_user(_x, _p) __put_user(_x, _p) #define clear_user(...) linux_clear_user(__VA_ARGS__) -#define access_ok(...) linux_access_ok(__VA_ARGS__) + +#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 50000 +#define access_ok(a,b) linux_access_ok(a,b) +#else +#define access_ok(a,b,c) linux_access_ok(b,c) +#endif extern int linux_copyin(const void *uaddr, void *kaddr, size_t len); extern int linux_copyout(const void *kaddr, void *uaddr, size_t len); extern size_t linux_clear_user(void *uaddr, size_t len); -extern int linux_access_ok(int rw, const void *uaddr, size_t len); +extern int linux_access_ok(const void *uaddr, size_t len); /* * NOTE: Each pagefault_disable() call must have a corresponding Index: sys/compat/linuxkpi/common/src/linux_compat.c =================================================================== --- sys/compat/linuxkpi/common/src/linux_compat.c +++ sys/compat/linuxkpi/common/src/linux_compat.c @@ -897,7 +897,7 @@ } int -linux_access_ok(int rw, const void *uaddr, size_t len) +linux_access_ok(const void *uaddr, size_t len) { uintptr_t saddr; uintptr_t eaddr; Index: sys/compat/linuxkpi/common/src/linux_current.c =================================================================== --- sys/compat/linuxkpi/common/src/linux_current.c +++ sys/compat/linuxkpi/common/src/linux_current.c @@ -67,6 +67,7 @@ ts->task_thread = td; ts->comm = td->td_name; ts->pid = td->td_tid; + ts->group_leader = ts; atomic_set(&ts->usage, 1); atomic_set(&ts->state, TASK_RUNNING); init_completion(&ts->parked); Index: sys/compat/linuxkpi/common/src/linux_tasklet.c =================================================================== --- sys/compat/linuxkpi/common/src/linux_tasklet.c +++ sys/compat/linuxkpi/common/src/linux_tasklet.c @@ -41,20 +41,22 @@ #define TASKLET_ST_BUSY 1 #define TASKLET_ST_EXEC 2 #define TASKLET_ST_LOOP 3 -#define TASKLET_ST_PAUSED 4 #define TASKLET_ST_CMPSET(ts, old, new) \ - atomic_cmpset_ptr((volatile uintptr_t *)&(ts)->entry.tqe_prev, old, new) + atomic_cmpset_int((volatile u_int *)&(ts)->_state, old, new) #define TASKLET_ST_SET(ts, new) \ - WRITE_ONCE(*(volatile uintptr_t *)&(ts)->entry.tqe_prev, new) + WRITE_ONCE(*(volatile u_int *)&(ts)->_state, new) #define TASKLET_ST_GET(ts) \ - READ_ONCE(*(volatile uintptr_t *)&(ts)->entry.tqe_prev) + READ_ONCE(*(volatile u_int *)&(ts)->_state) + +#define TASKLET_ST_TESTANDSET(ts, new) \ + atomic_testandset_int((volatile u_int *)&(ts)->_state, new) struct tasklet_worker { struct mtx mtx; - TAILQ_HEAD(, tasklet_struct) head; + TAILQ_HEAD(tasklet_list, tasklet_struct) head; struct grouptask gtask; } __aligned(CACHE_LINE_SIZE); @@ -68,25 +70,34 @@ { struct tasklet_worker *tw = (struct tasklet_worker *)arg; struct tasklet_struct *ts; + struct tasklet_struct *last; linux_set_current(curthread); TASKLET_WORKER_LOCK(tw); + last = TAILQ_LAST(&tw->head, tasklet_list); while (1) { ts = TAILQ_FIRST(&tw->head); if (ts == NULL) break; TAILQ_REMOVE(&tw->head, ts, entry); - TASKLET_WORKER_UNLOCK(tw); - do { - /* reset executing state */ - TASKLET_ST_SET(ts, TASKLET_ST_EXEC); - - ts->func(ts->data); - - } while (TASKLET_ST_CMPSET(ts, TASKLET_ST_EXEC, TASKLET_ST_IDLE) == 0); - TASKLET_WORKER_LOCK(tw); + if (!atomic_read(&ts->count)) { + TASKLET_WORKER_UNLOCK(tw); + do { + /* reset executing state */ + TASKLET_ST_SET(ts, TASKLET_ST_EXEC); + + ts->func(ts->data); + + } while (TASKLET_ST_CMPSET(ts, TASKLET_ST_EXEC, + TASKLET_ST_IDLE) == 0); + TASKLET_WORKER_LOCK(tw); + } else { + TAILQ_INSERT_TAIL(&tw->head, ts, entry); + } + if (ts == last) + break; } TASKLET_WORKER_UNLOCK(tw); } @@ -140,6 +151,8 @@ ts->entry.tqe_next = NULL; ts->func = func; ts->data = data; + atomic_set_int(&(ts)->_state, TASKLET_ST_IDLE); + atomic_set(&ts->count, 0); } void @@ -158,6 +171,10 @@ tasklet_schedule(struct tasklet_struct *ts) { + /* tasklet is paused */ + if (atomic_read(&ts->count)) + return; + if (TASKLET_ST_CMPSET(ts, TASKLET_ST_EXEC, TASKLET_ST_LOOP)) { /* tasklet_handler() will loop */ } else if (TASKLET_ST_CMPSET(ts, TASKLET_ST_IDLE, TASKLET_ST_BUSY)) { @@ -201,17 +218,47 @@ void tasklet_enable(struct tasklet_struct *ts) { - (void) TASKLET_ST_CMPSET(ts, TASKLET_ST_PAUSED, TASKLET_ST_IDLE); + + atomic_dec(&ts->count); +} + +bool +tasklet_is_enabled(struct tasklet_struct *ts) +{ + + return !atomic_read(&ts->count); } void tasklet_disable(struct tasklet_struct *ts) { - while (1) { - if (TASKLET_ST_GET(ts) == TASKLET_ST_PAUSED) - break; - if (TASKLET_ST_CMPSET(ts, TASKLET_ST_IDLE, TASKLET_ST_PAUSED)) - break; + + atomic_inc(&ts->count); + tasklet_unlock_wait(ts); + mb(); +} + +int +tasklet_trylock(struct tasklet_struct *ts) +{ + + return (!TASKLET_ST_TESTANDSET(ts, TASKLET_ST_BUSY)); +} + +void +tasklet_unlock(struct tasklet_struct *ts) +{ + + TASKLET_ST_SET(ts, TASKLET_ST_IDLE); +} + +void +tasklet_unlock_wait(struct tasklet_struct *ts) +{ + + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "tasklet_kill() can sleep"); + + /* wait until tasklet is no longer busy */ + while (TASKLET_ST_GET(ts) != TASKLET_ST_IDLE) pause("W", 1); - } }