xref: /dflybsd-src/sys/dev/drm/linux_rcu.c (revision 3f2dd94a569761201b5b0a18b2f697f97fe1b9dc)
18141ce26SMatthew Dillon /*
28141ce26SMatthew Dillon  * Copyright (c) 2020 The DragonFly Project.  All rights reserved.
38141ce26SMatthew Dillon  *
48141ce26SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
58141ce26SMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
68141ce26SMatthew Dillon  *
78141ce26SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
88141ce26SMatthew Dillon  * modification, are permitted provided that the following conditions
98141ce26SMatthew Dillon  * are met:
108141ce26SMatthew Dillon  *
118141ce26SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
128141ce26SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
138141ce26SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
148141ce26SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
158141ce26SMatthew Dillon  *    the documentation and/or other materials provided with the
168141ce26SMatthew Dillon  *    distribution.
178141ce26SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
188141ce26SMatthew Dillon  *    contributors may be used to endorse or promote products derived
198141ce26SMatthew Dillon  *    from this software without specific, prior written permission.
208141ce26SMatthew Dillon  *
218141ce26SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
228141ce26SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
238141ce26SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
248141ce26SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
258141ce26SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
268141ce26SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
278141ce26SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
288141ce26SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
298141ce26SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
308141ce26SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
318141ce26SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
328141ce26SMatthew Dillon  * SUCH DAMAGE.
338141ce26SMatthew Dillon  */
348141ce26SMatthew Dillon /*
358141ce26SMatthew Dillon  * Brute-force implementation for linux RCU functions.  Just use a delay
368141ce26SMatthew Dillon  * line and per-cpu callouts.
378141ce26SMatthew Dillon  */
388141ce26SMatthew Dillon #include <sys/types.h>
398141ce26SMatthew Dillon #include <sys/errno.h>
408141ce26SMatthew Dillon #include <sys/kernel.h>
418141ce26SMatthew Dillon #include <sys/spinlock.h>
428141ce26SMatthew Dillon #include <sys/spinlock2.h>
438141ce26SMatthew Dillon #include <linux/gfp.h>
448141ce26SMatthew Dillon #include <linux/slab.h>
458141ce26SMatthew Dillon #include <linux/rcupdate.h>
468141ce26SMatthew Dillon 
478141ce26SMatthew Dillon #include <machine/atomic.h>
488141ce26SMatthew Dillon 
498141ce26SMatthew Dillon typedef struct rcu_elm {
508141ce26SMatthew Dillon 	enum { RCU_NULL, RCU_CALL, RCU_FREE } type;
518141ce26SMatthew Dillon 	int	ticks;
528141ce26SMatthew Dillon 	void	(*func)(struct rcu_head *arg);
538141ce26SMatthew Dillon 	void	*ptr;
548141ce26SMatthew Dillon } rcu_elm_t;
558141ce26SMatthew Dillon 
568141ce26SMatthew Dillon typedef struct rcu_pcpu {
578141ce26SMatthew Dillon 	rcu_elm_t *elms;
588141ce26SMatthew Dillon 	int	size;
598141ce26SMatthew Dillon 	int	mask;
608141ce26SMatthew Dillon 	int	s;
618141ce26SMatthew Dillon 	int	e;
628141ce26SMatthew Dillon 	int	running;
638141ce26SMatthew Dillon 	struct callout timer_callout;
648141ce26SMatthew Dillon } rcu_pcpu_t;
658141ce26SMatthew Dillon 
668141ce26SMatthew Dillon static rcu_pcpu_t *rcupcpu;
678141ce26SMatthew Dillon 
688141ce26SMatthew Dillon /*
698141ce26SMatthew Dillon  * Timer callout (pcpu)
708141ce26SMatthew Dillon  */
718141ce26SMatthew Dillon static void
rcu_timer(void * arg)728141ce26SMatthew Dillon rcu_timer(void *arg)
738141ce26SMatthew Dillon {
748141ce26SMatthew Dillon 	rcu_pcpu_t *rcu = arg;
758141ce26SMatthew Dillon 	rcu_elm_t *elm;
768141ce26SMatthew Dillon 	int delta;
778141ce26SMatthew Dillon 
788141ce26SMatthew Dillon 	crit_enter();
798141ce26SMatthew Dillon 	while (rcu->s != rcu->e) {
808141ce26SMatthew Dillon 		elm = &rcu->elms[rcu->s & rcu->mask];
818141ce26SMatthew Dillon 		delta = ticks - elm->ticks;	/* 2s compl underflow */
828141ce26SMatthew Dillon 		if (delta < hz)
838141ce26SMatthew Dillon 			break;
848141ce26SMatthew Dillon 
858141ce26SMatthew Dillon 		switch(elm->type) {
868141ce26SMatthew Dillon 		case RCU_NULL:
878141ce26SMatthew Dillon 			break;
888141ce26SMatthew Dillon 		case RCU_CALL:
898141ce26SMatthew Dillon 			elm->func(elm->ptr);
908141ce26SMatthew Dillon 			break;
918141ce26SMatthew Dillon 		case RCU_FREE:
928141ce26SMatthew Dillon 			kfree(elm->ptr);
938141ce26SMatthew Dillon 			break;
948141ce26SMatthew Dillon 		}
958141ce26SMatthew Dillon 		elm->type = RCU_NULL;
968141ce26SMatthew Dillon 		++rcu->s;
978141ce26SMatthew Dillon 	}
988141ce26SMatthew Dillon 	if (rcu->s == rcu->e) {
998141ce26SMatthew Dillon 		rcu->running = 0;
1008141ce26SMatthew Dillon 	} else {
1018141ce26SMatthew Dillon 		callout_reset_bycpu(&rcu->timer_callout, hz / 10, rcu_timer,
1028141ce26SMatthew Dillon 				    rcu, mycpuid);
1038141ce26SMatthew Dillon 	}
1048141ce26SMatthew Dillon 	crit_exit();
1058141ce26SMatthew Dillon }
1068141ce26SMatthew Dillon 
1078141ce26SMatthew Dillon 
1088141ce26SMatthew Dillon /*
1098141ce26SMatthew Dillon  * Ping timer callout (pcpu).
1108141ce26SMatthew Dillon  *
1118141ce26SMatthew Dillon  * Must be in critical section.
1128141ce26SMatthew Dillon  */
1138141ce26SMatthew Dillon static void
rcu_ping(rcu_pcpu_t * rcu)1148141ce26SMatthew Dillon rcu_ping(rcu_pcpu_t *rcu)
1158141ce26SMatthew Dillon {
1168141ce26SMatthew Dillon 	if (rcu->running == 0) {
1178141ce26SMatthew Dillon 		rcu->running = 1;
1188141ce26SMatthew Dillon 		callout_reset_bycpu(&rcu->timer_callout, hz / 10, rcu_timer,
1198141ce26SMatthew Dillon 				    rcu, mycpuid);
1208141ce26SMatthew Dillon 	}
1218141ce26SMatthew Dillon }
1228141ce26SMatthew Dillon 
1238141ce26SMatthew Dillon /*
1248141ce26SMatthew Dillon  * Expand the rcu array for the current cpu
1258141ce26SMatthew Dillon  */
1268141ce26SMatthew Dillon static void
rcu_expand(rcu_pcpu_t * rcu)1278141ce26SMatthew Dillon rcu_expand(rcu_pcpu_t *rcu)
1288141ce26SMatthew Dillon {
1298141ce26SMatthew Dillon 	rcu_elm_t *oelms;
1308141ce26SMatthew Dillon 	rcu_elm_t *nelms;
1318141ce26SMatthew Dillon 	int count;
1328141ce26SMatthew Dillon 	int nsize;
1338141ce26SMatthew Dillon 	int nmask;
1348141ce26SMatthew Dillon 	int n;
1358141ce26SMatthew Dillon 
1368141ce26SMatthew Dillon 	count = rcu->e - rcu->s;	/* note: 2s complement underflow */
1378141ce26SMatthew Dillon 	while (unlikely(count == rcu->size)) {
1388141ce26SMatthew Dillon 		nsize = count ? count * 2 : 16;
1398141ce26SMatthew Dillon 		nelms = kzalloc(nsize * sizeof(*nelms), GFP_KERNEL);
1408141ce26SMatthew Dillon 		kprintf("drm: expand RCU cpu %d to %d\n", mycpuid, nsize);
1418141ce26SMatthew Dillon 		if (likely(count == rcu->size)) {
1428141ce26SMatthew Dillon 			nmask = nsize - 1;
1438141ce26SMatthew Dillon 			oelms = rcu->elms;
1448141ce26SMatthew Dillon 			n = rcu->s;
1458141ce26SMatthew Dillon 			while (n != rcu->e) {
1468141ce26SMatthew Dillon 				nelms[n & nmask] = oelms[n & rcu->mask];
1478141ce26SMatthew Dillon 				++n;
1488141ce26SMatthew Dillon 			}
1498141ce26SMatthew Dillon 			rcu->elms = nelms;
1508141ce26SMatthew Dillon 			rcu->size = nsize;
1518141ce26SMatthew Dillon 			rcu->mask = nmask;
1528141ce26SMatthew Dillon 			nelms = oelms;
1538141ce26SMatthew Dillon 		}
1548141ce26SMatthew Dillon 		if (likely(nelms != NULL))
1558141ce26SMatthew Dillon 			kfree(nelms);
1568141ce26SMatthew Dillon 		count = rcu->e - rcu->s;
1578141ce26SMatthew Dillon 	}
1588141ce26SMatthew Dillon 	KKASSERT(count >= 0 && count < rcu->size);
1598141ce26SMatthew Dillon }
1608141ce26SMatthew Dillon 
1618141ce26SMatthew Dillon void
__kfree_rcu(void * ptr)162*3f2dd94aSFrançois Tigeot __kfree_rcu(void *ptr)
1638141ce26SMatthew Dillon {
1648141ce26SMatthew Dillon 	rcu_pcpu_t *rcu;
1658141ce26SMatthew Dillon 	rcu_elm_t *elm;
1668141ce26SMatthew Dillon 
1678141ce26SMatthew Dillon 	if (unlikely(rcupcpu == NULL)) {
1688141ce26SMatthew Dillon 		kfree(ptr);
1698141ce26SMatthew Dillon 		return;
1708141ce26SMatthew Dillon 	}
1718141ce26SMatthew Dillon 	rcu = &rcupcpu[mycpuid];
1728141ce26SMatthew Dillon 
1738141ce26SMatthew Dillon 	crit_enter();
1748141ce26SMatthew Dillon 	rcu_expand(rcu);
1758141ce26SMatthew Dillon 	elm = &rcu->elms[rcu->e & rcu->mask];
1768141ce26SMatthew Dillon 	++rcu->e;
1778141ce26SMatthew Dillon 
1788141ce26SMatthew Dillon 	elm->type = RCU_FREE;
1798141ce26SMatthew Dillon 	elm->ticks = ticks;
1808141ce26SMatthew Dillon 	elm->ptr = ptr;
1818141ce26SMatthew Dillon 
1828141ce26SMatthew Dillon 	rcu_ping(rcu);
1838141ce26SMatthew Dillon 	crit_exit();
1848141ce26SMatthew Dillon }
1858141ce26SMatthew Dillon 
1868141ce26SMatthew Dillon void
call_rcu(struct rcu_head * head,void (* func)(struct rcu_head *))1878141ce26SMatthew Dillon call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *))
1888141ce26SMatthew Dillon {
1898141ce26SMatthew Dillon 	rcu_pcpu_t *rcu;
1908141ce26SMatthew Dillon 	rcu_elm_t *elm;
1918141ce26SMatthew Dillon 
1928141ce26SMatthew Dillon 	if (unlikely(rcupcpu == NULL)) {
1938141ce26SMatthew Dillon 		func(head);
1948141ce26SMatthew Dillon 		return;
1958141ce26SMatthew Dillon 	}
1968141ce26SMatthew Dillon 	rcu = &rcupcpu[mycpuid];
1978141ce26SMatthew Dillon 
1988141ce26SMatthew Dillon 	crit_enter();
1998141ce26SMatthew Dillon 	rcu_expand(rcu);
2008141ce26SMatthew Dillon 	elm = &rcu->elms[rcu->e & rcu->mask];
2018141ce26SMatthew Dillon 	++rcu->e;
2028141ce26SMatthew Dillon 
2038141ce26SMatthew Dillon 	elm->type = RCU_CALL;
2048141ce26SMatthew Dillon 	elm->ticks = ticks;
2058141ce26SMatthew Dillon 	elm->func = func;
2068141ce26SMatthew Dillon 	elm->ptr = head;
2078141ce26SMatthew Dillon 
2088141ce26SMatthew Dillon 	rcu_ping(rcu);
2098141ce26SMatthew Dillon 	crit_exit();
2108141ce26SMatthew Dillon }
2118141ce26SMatthew Dillon 
2128141ce26SMatthew Dillon static int
init_rcu(void * dummy __unused)2138141ce26SMatthew Dillon init_rcu(void *dummy __unused)
2148141ce26SMatthew Dillon {
2158141ce26SMatthew Dillon 	int i;
2168141ce26SMatthew Dillon 
2178141ce26SMatthew Dillon 	rcupcpu = kzalloc(ncpus * sizeof(*rcupcpu), GFP_KERNEL);
2188141ce26SMatthew Dillon 	for (i = 0; i < ncpus; ++i) {
2198141ce26SMatthew Dillon 		callout_init_mp(&rcupcpu[i].timer_callout);
2208141ce26SMatthew Dillon 	}
2218141ce26SMatthew Dillon 	return 0;
2228141ce26SMatthew Dillon }
2238141ce26SMatthew Dillon 
2248141ce26SMatthew Dillon SYSINIT(linux_rcu_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, init_rcu, NULL);
225