xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_atomic64.c (revision b12f5a54412d8c7b2407b02b130ba3b4ccd6b7c2)
1*b12f5a54Sriastradh /*	$NetBSD: linux_atomic64.c,v 1.3 2018/08/27 15:11:17 riastradh Exp $	*/
21d9ce707Sriastradh 
31d9ce707Sriastradh /*-
41d9ce707Sriastradh  * Copyright (c) 2018 The NetBSD Foundation, Inc.
51d9ce707Sriastradh  * All rights reserved.
61d9ce707Sriastradh  *
71d9ce707Sriastradh  * This code is derived from software contributed to The NetBSD Foundation
81d9ce707Sriastradh  * by Taylor R. Campbell.
91d9ce707Sriastradh  *
101d9ce707Sriastradh  * Redistribution and use in source and binary forms, with or without
111d9ce707Sriastradh  * modification, are permitted provided that the following conditions
121d9ce707Sriastradh  * are met:
131d9ce707Sriastradh  * 1. Redistributions of source code must retain the above copyright
141d9ce707Sriastradh  *    notice, this list of conditions and the following disclaimer.
151d9ce707Sriastradh  * 2. Redistributions in binary form must reproduce the above copyright
161d9ce707Sriastradh  *    notice, this list of conditions and the following disclaimer in the
171d9ce707Sriastradh  *    documentation and/or other materials provided with the distribution.
181d9ce707Sriastradh  *
191d9ce707Sriastradh  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
201d9ce707Sriastradh  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
211d9ce707Sriastradh  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
221d9ce707Sriastradh  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
231d9ce707Sriastradh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
241d9ce707Sriastradh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
251d9ce707Sriastradh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
261d9ce707Sriastradh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
271d9ce707Sriastradh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
281d9ce707Sriastradh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
291d9ce707Sriastradh  * POSSIBILITY OF SUCH DAMAGE.
301d9ce707Sriastradh  */
311d9ce707Sriastradh 
321d9ce707Sriastradh #include <sys/cdefs.h>
33*b12f5a54Sriastradh __KERNEL_RCSID(0, "$NetBSD: linux_atomic64.c,v 1.3 2018/08/27 15:11:17 riastradh Exp $");
341d9ce707Sriastradh 
351d9ce707Sriastradh #include <sys/param.h>
361d9ce707Sriastradh #include <sys/bitops.h>
371d9ce707Sriastradh #include <sys/lock.h>
381d9ce707Sriastradh 
391d9ce707Sriastradh #include <linux/atomic.h>
401d9ce707Sriastradh 
411d9ce707Sriastradh #ifdef __HAVE_ATOMIC64_OPS
421d9ce707Sriastradh 
431d9ce707Sriastradh int
linux_atomic64_init(void)441d9ce707Sriastradh linux_atomic64_init(void)
451d9ce707Sriastradh {
461d9ce707Sriastradh 	return 0;
471d9ce707Sriastradh }
481d9ce707Sriastradh 
491d9ce707Sriastradh void
linux_atomic64_fini(void)501d9ce707Sriastradh linux_atomic64_fini(void)
511d9ce707Sriastradh {
521d9ce707Sriastradh }
531d9ce707Sriastradh 
541d9ce707Sriastradh #else
551d9ce707Sriastradh 
561d9ce707Sriastradh static struct {
571d9ce707Sriastradh 	kmutex_t	lock;
581d9ce707Sriastradh 	uint32_t	gen;	/* for unlocked read */
591d9ce707Sriastradh 	char		pad[CACHE_LINE_SIZE -
601d9ce707Sriastradh 			    sizeof(kmutex_t) - sizeof(uint32_t)];
611d9ce707Sriastradh } atomic64_tab[PAGE_SIZE/CACHE_LINE_SIZE] __cacheline_aligned;
621d9ce707Sriastradh CTASSERT(sizeof(atomic64_tab) == PAGE_SIZE);
631d9ce707Sriastradh CTASSERT(sizeof(atomic64_tab[0]) == CACHE_LINE_SIZE);
641d9ce707Sriastradh 
651d9ce707Sriastradh int
linux_atomic64_init(void)661d9ce707Sriastradh linux_atomic64_init(void)
671d9ce707Sriastradh {
681d9ce707Sriastradh 	size_t i;
691d9ce707Sriastradh 
701d9ce707Sriastradh 	for (i = 0; i < __arraycount(atomic64_tab); i++) {
711d9ce707Sriastradh 		mutex_init(&atomic64_tab[i].lock, MUTEX_DEFAULT, IPL_HIGH);
721d9ce707Sriastradh 		atomic64_tab[i].gen = 0;
731d9ce707Sriastradh 	}
741d9ce707Sriastradh 
751d9ce707Sriastradh 	return 0;
761d9ce707Sriastradh }
771d9ce707Sriastradh 
781d9ce707Sriastradh void
linux_atomic64_fini(void)791d9ce707Sriastradh linux_atomic64_fini(void)
801d9ce707Sriastradh {
811d9ce707Sriastradh 	size_t i;
821d9ce707Sriastradh 
831d9ce707Sriastradh 	for (i = 0; i < __arraycount(atomic64_tab); i++) {
841d9ce707Sriastradh 		KASSERT((atomic64_tab[i].gen & 1) == 0);
851d9ce707Sriastradh 		mutex_destroy(&atomic64_tab[i].lock);
861d9ce707Sriastradh 	}
871d9ce707Sriastradh }
881d9ce707Sriastradh 
891d9ce707Sriastradh static inline size_t
atomic64_hash(const struct atomic64 * a)901d9ce707Sriastradh atomic64_hash(const struct atomic64 *a)
911d9ce707Sriastradh {
921d9ce707Sriastradh 
931d9ce707Sriastradh 	return ((uintptr_t)a >> ilog2(CACHE_LINE_SIZE)) %
941d9ce707Sriastradh 	    __arraycount(atomic64_tab);
951d9ce707Sriastradh }
961d9ce707Sriastradh 
971d9ce707Sriastradh static void
atomic64_lock(struct atomic64 * a)981d9ce707Sriastradh atomic64_lock(struct atomic64 *a)
991d9ce707Sriastradh {
1001d9ce707Sriastradh 	size_t i = atomic64_hash(a);
1011d9ce707Sriastradh 
1021d9ce707Sriastradh 	mutex_spin_enter(&atomic64_tab[i].lock);
1031d9ce707Sriastradh 	KASSERT((atomic64_tab[i].gen & 1) == 0);
1041d9ce707Sriastradh 	atomic64_tab[i].gen |= 1;
1051d9ce707Sriastradh 	membar_producer();
1061d9ce707Sriastradh }
1071d9ce707Sriastradh 
1081d9ce707Sriastradh static void
atomic64_unlock(struct atomic64 * a)1091d9ce707Sriastradh atomic64_unlock(struct atomic64 *a)
1101d9ce707Sriastradh {
1111d9ce707Sriastradh 	size_t i = atomic64_hash(a);
1121d9ce707Sriastradh 
1131d9ce707Sriastradh 	KASSERT(mutex_owned(&atomic64_tab[i].lock));
1141d9ce707Sriastradh 	KASSERT((atomic64_tab[i].gen & 1) == 1);
1151d9ce707Sriastradh 
1161d9ce707Sriastradh 	membar_producer();
1171d9ce707Sriastradh 	atomic64_tab[i].gen |= 1; /* paranoia */
1181d9ce707Sriastradh 	atomic64_tab[i].gen++;
1191d9ce707Sriastradh 	mutex_spin_exit(&atomic64_tab[i].lock);
1201d9ce707Sriastradh }
1211d9ce707Sriastradh 
1221d9ce707Sriastradh uint64_t
atomic64_read(const struct atomic64 * a)1231d9ce707Sriastradh atomic64_read(const struct atomic64 *a)
1241d9ce707Sriastradh {
1251d9ce707Sriastradh 	size_t i = atomic64_hash(a);
1261d9ce707Sriastradh 	uint32_t gen;
1271d9ce707Sriastradh 	uint64_t value;
1281d9ce707Sriastradh 
1291d9ce707Sriastradh 	do {
1301d9ce707Sriastradh 		while (__predict_false((gen = atomic64_tab[i].gen) & 1))
1311d9ce707Sriastradh 			SPINLOCK_BACKOFF_HOOK;
1321d9ce707Sriastradh 		membar_consumer();
1331d9ce707Sriastradh 		value = a->a_v;
1341d9ce707Sriastradh 		membar_consumer();
1351d9ce707Sriastradh 	} while (__predict_false(atomic64_tab[i].gen != gen));
1361d9ce707Sriastradh 
1371d9ce707Sriastradh 	return value;
1381d9ce707Sriastradh }
1391d9ce707Sriastradh 
1401d9ce707Sriastradh void
atomic64_set(struct atomic64 * a,uint64_t value)1411d9ce707Sriastradh atomic64_set(struct atomic64 *a, uint64_t value)
1421d9ce707Sriastradh {
1431d9ce707Sriastradh 
1441d9ce707Sriastradh 	atomic64_lock(a);
1451d9ce707Sriastradh 	a->a_v = value;
1461d9ce707Sriastradh 	atomic64_unlock(a);
1471d9ce707Sriastradh }
1481d9ce707Sriastradh 
1491d9ce707Sriastradh void
atomic64_add(int64_t delta,struct atomic64 * a)15088b7f7daSriastradh atomic64_add(int64_t delta, struct atomic64 *a)
1511d9ce707Sriastradh {
1521d9ce707Sriastradh 
1531d9ce707Sriastradh 	atomic64_lock(a);
1541d9ce707Sriastradh 	a->a_v += delta;
1551d9ce707Sriastradh 	atomic64_unlock(a);
1561d9ce707Sriastradh }
1571d9ce707Sriastradh 
1581d9ce707Sriastradh void
atomic64_sub(int64_t delta,struct atomic64 * a)15988b7f7daSriastradh atomic64_sub(int64_t delta, struct atomic64 *a)
1601d9ce707Sriastradh {
1611d9ce707Sriastradh 
1621d9ce707Sriastradh 	atomic64_lock(a);
1631d9ce707Sriastradh 	a->a_v -= delta;
1641d9ce707Sriastradh 	atomic64_unlock(a);
1651d9ce707Sriastradh }
1661d9ce707Sriastradh 
167*b12f5a54Sriastradh int64_t
atomic64_add_return(int64_t delta,struct atomic64 * a)168*b12f5a54Sriastradh atomic64_add_return(int64_t delta, struct atomic64 *a)
169*b12f5a54Sriastradh {
170*b12f5a54Sriastradh 	int64_t v;
171*b12f5a54Sriastradh 
172*b12f5a54Sriastradh 	atomic64_lock(a);
173*b12f5a54Sriastradh 	v = (int64_t)(a->a_v += delta);
174*b12f5a54Sriastradh 	atomic64_unlock(a);
175*b12f5a54Sriastradh 
176*b12f5a54Sriastradh 	return v;
177*b12f5a54Sriastradh }
178*b12f5a54Sriastradh 
1791d9ce707Sriastradh uint64_t
atomic64_xchg(struct atomic64 * a,uint64_t new)1801d9ce707Sriastradh atomic64_xchg(struct atomic64 *a, uint64_t new)
1811d9ce707Sriastradh {
1821d9ce707Sriastradh 	uint64_t old;
1831d9ce707Sriastradh 
1841d9ce707Sriastradh 	atomic64_lock(a);
1851d9ce707Sriastradh 	old = a->a_v;
1861d9ce707Sriastradh 	a->a_v = new;
1871d9ce707Sriastradh 	atomic64_unlock(a);
1881d9ce707Sriastradh 
1891d9ce707Sriastradh 	return old;
1901d9ce707Sriastradh }
1911d9ce707Sriastradh 
1921d9ce707Sriastradh uint64_t
atomic64_cmpxchg(struct atomic64 * a,uint64_t expect,uint64_t new)1931d9ce707Sriastradh atomic64_cmpxchg(struct atomic64 *a, uint64_t expect, uint64_t new)
1941d9ce707Sriastradh {
1951d9ce707Sriastradh 	uint64_t old;
1961d9ce707Sriastradh 
1971d9ce707Sriastradh 	atomic64_lock(a);
1981d9ce707Sriastradh 	old = a->a_v;
1991d9ce707Sriastradh 	if (old == expect)
2001d9ce707Sriastradh 		a->a_v = new;
2011d9ce707Sriastradh 	atomic64_unlock(a);
2021d9ce707Sriastradh 
2031d9ce707Sriastradh 	return old;
2041d9ce707Sriastradh }
2051d9ce707Sriastradh 
2061d9ce707Sriastradh #endif
207