177a6b00eSFrançois Tigeot /* 277a6b00eSFrançois Tigeot * Copyright (c) 2020 François Tigeot <ftigeot@wolfpond.org> 377a6b00eSFrançois Tigeot * All rights reserved. 477a6b00eSFrançois Tigeot * 577a6b00eSFrançois Tigeot * Redistribution and use in source and binary forms, with or without 677a6b00eSFrançois Tigeot * modification, are permitted provided that the following conditions 777a6b00eSFrançois Tigeot * are met: 877a6b00eSFrançois Tigeot * 1. Redistributions of source code must retain the above copyright 977a6b00eSFrançois Tigeot * notice unmodified, this list of conditions, and the following 1077a6b00eSFrançois Tigeot * disclaimer. 1177a6b00eSFrançois Tigeot * 2. Redistributions in binary form must reproduce the above copyright 1277a6b00eSFrançois Tigeot * notice, this list of conditions and the following disclaimer in the 1377a6b00eSFrançois Tigeot * documentation and/or other materials provided with the distribution. 1477a6b00eSFrançois Tigeot * 1577a6b00eSFrançois Tigeot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1677a6b00eSFrançois Tigeot * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1777a6b00eSFrançois Tigeot * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1877a6b00eSFrançois Tigeot * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1977a6b00eSFrançois Tigeot * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2077a6b00eSFrançois Tigeot * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2177a6b00eSFrançois Tigeot * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2277a6b00eSFrançois Tigeot * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2377a6b00eSFrançois Tigeot * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2477a6b00eSFrançois Tigeot * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2577a6b00eSFrançois Tigeot */ 2677a6b00eSFrançois Tigeot 2777a6b00eSFrançois Tigeot #include <linux/interrupt.h> 2877a6b00eSFrançois Tigeot #include <linux/slab.h> 2977a6b00eSFrançois Tigeot 3077a6b00eSFrançois Tigeot #include <sys/kthread.h> 3177a6b00eSFrançois Tigeot 3277a6b00eSFrançois Tigeot /* 3377a6b00eSFrançois Tigeot * Linux tasklet constraints: 3477a6b00eSFrançois Tigeot * - tasklets that have the same type cannot be run on multiple processors at 3577a6b00eSFrançois Tigeot * the same time 3677a6b00eSFrançois Tigeot * - tasklets always run on the processor from which they were originally 3777a6b00eSFrançois Tigeot * submitted 3877a6b00eSFrançois Tigeot * - when a tasklet is scheduled, its state is set to TASKLET_STATE_SCHED, 3977a6b00eSFrançois Tigeot * and the tasklet added to a queue 4077a6b00eSFrançois Tigeot * - during the execution of its function, the tasklet state is set to 4177a6b00eSFrançois Tigeot * TASKLET_STATE_RUN and the TASKLET_STATE_SCHED state is removed 4277a6b00eSFrançois Tigeot */ 4377a6b00eSFrançois Tigeot 4477a6b00eSFrançois Tigeot struct tasklet_entry { 4577a6b00eSFrançois Tigeot struct tasklet_struct *ts; 46*e071d366SFrançois Tigeot STAILQ_ENTRY(tasklet_entry) tasklet_entries; 4777a6b00eSFrançois Tigeot }; 4877a6b00eSFrançois Tigeot 4977a6b00eSFrançois Tigeot static struct lock tasklet_lock = LOCK_INITIALIZER("dltll", 0, LK_CANRECURSE); 5077a6b00eSFrançois Tigeot 5177a6b00eSFrançois Tigeot static struct thread *tasklet_td = NULL; 52*e071d366SFrançois Tigeot STAILQ_HEAD(tasklet_list_head, tasklet_entry) tlist = STAILQ_HEAD_INITIALIZER(tlist); 53*e071d366SFrançois Tigeot STAILQ_HEAD(tasklet_hi_list_head, tasklet_entry) tlist_hi = STAILQ_HEAD_INITIALIZER(tlist_hi); 5477a6b00eSFrançois Tigeot 5577a6b00eSFrançois Tigeot static int tasklet_pending = 0; 5677a6b00eSFrançois Tigeot 5777a6b00eSFrançois Tigeot #define PROCESS_TASKLET_LIST(which_list) do { \ 58*e071d366SFrançois Tigeot STAILQ_FOREACH_MUTABLE(te, &which_list, tasklet_entries, tmp_te) { \ 5977a6b00eSFrançois Tigeot struct tasklet_struct *t = te->ts; \ 6077a6b00eSFrançois Tigeot \ 6177a6b00eSFrançois Tigeot /* \ 6277a6b00eSFrançois Tigeot This tasklet is dying, remove it from the list. \ 6377a6b00eSFrançois Tigeot We allow to it to run one last time if it has \ 6477a6b00eSFrançois Tigeot already been scheduled. \ 6577a6b00eSFrançois Tigeot */ \ 6677a6b00eSFrançois Tigeot if (test_bit(TASKLET_IS_DYING, &t->state)) { \ 67*e071d366SFrançois Tigeot STAILQ_REMOVE(&which_list, te, tasklet_entry, tasklet_entries); \ 6877a6b00eSFrançois Tigeot kfree(te); \ 6977a6b00eSFrançois Tigeot } \ 7077a6b00eSFrançois Tigeot \ 71802fda02SFrançois Tigeot /* This tasklet is not enabled, try the next one */ \ 72802fda02SFrançois Tigeot if (atomic_read(&t->count) != 0) \ 73802fda02SFrançois Tigeot continue; \ 74802fda02SFrançois Tigeot \ 7577a6b00eSFrançois Tigeot /* This tasklet is not scheduled, try the next one */ \ 7677a6b00eSFrançois Tigeot if (!test_bit(TASKLET_STATE_SCHED, &t->state)) \ 7777a6b00eSFrançois Tigeot continue; \ 7877a6b00eSFrançois Tigeot \ 7977a6b00eSFrançois Tigeot clear_bit(TASKLET_STATE_SCHED, &t->state); \ 8077a6b00eSFrançois Tigeot set_bit(TASKLET_STATE_RUN, &t->state); \ 8177a6b00eSFrançois Tigeot \ 8277a6b00eSFrançois Tigeot lockmgr(&tasklet_lock, LK_RELEASE); \ 8377a6b00eSFrançois Tigeot if (t->func) \ 8477a6b00eSFrançois Tigeot t->func(t->data); \ 8577a6b00eSFrançois Tigeot lockmgr(&tasklet_lock, LK_EXCLUSIVE); \ 8677a6b00eSFrançois Tigeot \ 8777a6b00eSFrançois Tigeot clear_bit(TASKLET_STATE_RUN, &t->state); \ 8877a6b00eSFrançois Tigeot } \ 8977a6b00eSFrançois Tigeot } while (0) 9077a6b00eSFrançois Tigeot 9177a6b00eSFrançois Tigeot /* XXX runners should be CPU-specific */ 9277a6b00eSFrançois Tigeot static void 9377a6b00eSFrançois Tigeot tasklet_runner(void *arg) 9477a6b00eSFrançois Tigeot { 9577a6b00eSFrançois Tigeot struct tasklet_entry *te, *tmp_te; 9677a6b00eSFrançois Tigeot 9777a6b00eSFrançois Tigeot lockmgr(&tasklet_lock, LK_EXCLUSIVE); 9877a6b00eSFrançois Tigeot while (1) { 9977a6b00eSFrançois Tigeot /* 10077a6b00eSFrançois Tigeot Only sleep if we haven't been raced by a _schedule() 10177a6b00eSFrançois Tigeot call during an unlock window 10277a6b00eSFrançois Tigeot */ 10377a6b00eSFrançois Tigeot if (tasklet_pending == 0) { 10477a6b00eSFrançois Tigeot lksleep(&tasklet_runner, &tasklet_lock, 0, "tkidle", 0); 10577a6b00eSFrançois Tigeot } 10677a6b00eSFrançois Tigeot tasklet_pending = 0; 10777a6b00eSFrançois Tigeot 10877a6b00eSFrançois Tigeot /* Process hi tasklets first */ 10977a6b00eSFrançois Tigeot PROCESS_TASKLET_LIST(tlist_hi); 11077a6b00eSFrançois Tigeot PROCESS_TASKLET_LIST(tlist); 11177a6b00eSFrançois Tigeot } 11277a6b00eSFrançois Tigeot lockmgr(&tasklet_lock, LK_RELEASE); 11377a6b00eSFrançois Tigeot } 11477a6b00eSFrançois Tigeot 11577a6b00eSFrançois Tigeot void 11677a6b00eSFrançois Tigeot tasklet_init(struct tasklet_struct *t, 11777a6b00eSFrançois Tigeot void (*func)(unsigned long), unsigned long data) 11877a6b00eSFrançois Tigeot { 11977a6b00eSFrançois Tigeot t->state = 0; 12077a6b00eSFrançois Tigeot t->func = func; 12177a6b00eSFrançois Tigeot t->data = data; 122802fda02SFrançois Tigeot atomic_set(&t->count, 0); 12377a6b00eSFrançois Tigeot } 12477a6b00eSFrançois Tigeot 12565119432SFrançois Tigeot #define TASKLET_SCHEDULE_COMMON(t, list) do { \ 12665119432SFrançois Tigeot struct tasklet_entry *te; \ 12765119432SFrançois Tigeot \ 12865119432SFrançois Tigeot lockmgr(&tasklet_lock, LK_EXCLUSIVE); \ 12965119432SFrançois Tigeot set_bit(TASKLET_STATE_SCHED, &t->state); \ 13065119432SFrançois Tigeot \ 131*e071d366SFrançois Tigeot STAILQ_FOREACH(te, &(list), tasklet_entries) { \ 13265119432SFrançois Tigeot if (te->ts == t) \ 13365119432SFrançois Tigeot goto found_and_done; \ 13465119432SFrançois Tigeot } \ 13565119432SFrançois Tigeot \ 13665119432SFrançois Tigeot te = kzalloc(sizeof(struct tasklet_entry), M_WAITOK); \ 13765119432SFrançois Tigeot te->ts = t; \ 138*e071d366SFrançois Tigeot STAILQ_INSERT_TAIL(&(list), te, tasklet_entries); \ 13965119432SFrançois Tigeot \ 14065119432SFrançois Tigeot found_and_done: \ 14165119432SFrançois Tigeot tasklet_pending = 1; \ 14265119432SFrançois Tigeot wakeup(&tasklet_runner); \ 14365119432SFrançois Tigeot lockmgr(&tasklet_lock, LK_RELEASE); \ 14465119432SFrançois Tigeot } while (0) 14565119432SFrançois Tigeot 14677a6b00eSFrançois Tigeot void 14777a6b00eSFrançois Tigeot tasklet_schedule(struct tasklet_struct *t) 14877a6b00eSFrançois Tigeot { 14965119432SFrançois Tigeot TASKLET_SCHEDULE_COMMON(t, tlist); 15077a6b00eSFrançois Tigeot } 15177a6b00eSFrançois Tigeot 15277a6b00eSFrançois Tigeot void 15377a6b00eSFrançois Tigeot tasklet_hi_schedule(struct tasklet_struct *t) 15477a6b00eSFrançois Tigeot { 15565119432SFrançois Tigeot TASKLET_SCHEDULE_COMMON(t, tlist_hi); 15677a6b00eSFrançois Tigeot } 15777a6b00eSFrançois Tigeot 15877a6b00eSFrançois Tigeot void 15977a6b00eSFrançois Tigeot tasklet_kill(struct tasklet_struct *t) 16077a6b00eSFrançois Tigeot { 16177a6b00eSFrançois Tigeot set_bit(TASKLET_IS_DYING, &t->state); 16277a6b00eSFrançois Tigeot wakeup(&tasklet_runner); 16377a6b00eSFrançois Tigeot } 16477a6b00eSFrançois Tigeot 16577a6b00eSFrançois Tigeot static int init_tasklets(void *arg) 16677a6b00eSFrançois Tigeot { 16777a6b00eSFrançois Tigeot kthread_create(tasklet_runner, NULL, &tasklet_td, "tasklet_runner"); 16877a6b00eSFrançois Tigeot 16977a6b00eSFrançois Tigeot return 0; 17077a6b00eSFrançois Tigeot } 17177a6b00eSFrançois Tigeot 17277a6b00eSFrançois Tigeot SYSINIT(linux_tasklet_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, init_tasklets, NULL); 173