Sun Dec 19 01:37:28 2021 UTC ()
Move Linux SRCU to common.


(riastradh)
diff -r1.3 -r1.4 src/sys/external/bsd/common/conf/files.linux
diff -r0 -r1.1 src/sys/external/bsd/common/include/linux/srcu.h
diff -r0 -r1.1 src/sys/external/bsd/common/linux/linux_srcu.c
diff -r1.2 -r0 src/sys/external/bsd/drm2/include/linux/srcu.h
diff -r1.22 -r1.23 src/sys/external/bsd/drm2/linux/files.drmkms_linux
diff -r1.1 -r0 src/sys/external/bsd/drm2/linux/linux_srcu.c

cvs diff -r1.3 -r1.4 src/sys/external/bsd/common/conf/files.linux (expand / switch to unified diff)

--- src/sys/external/bsd/common/conf/files.linux 2021/12/19 01:33:17 1.3
+++ src/sys/external/bsd/common/conf/files.linux 2021/12/19 01:37:27 1.4
@@ -1,9 +1,10 @@ @@ -1,9 +1,10 @@
1# $NetBSD: files.linux,v 1.3 2021/12/19 01:33:17 riastradh Exp $ 1# $NetBSD: files.linux,v 1.4 2021/12/19 01:37:27 riastradh Exp $
2 2
3define linux 3define linux
4 4
5makeoptions linux CPPFLAGS+="-I$S/external/bsd/common/include" 5makeoptions linux CPPFLAGS+="-I$S/external/bsd/common/include"
6 6
7file external/bsd/common/linux/linux_rcu.c linux 7file external/bsd/common/linux/linux_rcu.c linux
 8file external/bsd/common/linux/linux_srcu.c linux
8file external/bsd/common/linux/linux_tasklet.c linux 9file external/bsd/common/linux/linux_tasklet.c linux
9file external/bsd/common/linux/linux_work.c linux 10file external/bsd/common/linux/linux_work.c linux

File Added: src/sys/external/bsd/common/include/linux/srcu.h
/*	$NetBSD: srcu.h,v 1.1 2021/12/19 01:37:27 riastradh Exp $	*/

/*-
 * Copyright (c) 2018 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Taylor R. Campbell.
 *
 * 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, 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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.
 */

#ifndef	_LINUX_SRCU_H_
#define	_LINUX_SRCU_H_

#include <sys/types.h>
#include <sys/condvar.h>
#include <sys/mutex.h>

struct lwp;
struct percpu;

struct srcu {
	struct percpu		*srcu_percpu;	/* struct srcu_cpu */
	kmutex_t		srcu_lock;
	kcondvar_t		srcu_cv;
	struct lwp		*srcu_sync;
	int64_t			srcu_total;
	volatile unsigned	srcu_gen;
};

void	srcu_init(struct srcu *, const char *);
void	srcu_fini(struct srcu *);

int	srcu_read_lock(struct srcu *);
void	srcu_read_unlock(struct srcu *, int);

void	synchronize_srcu(struct srcu *);

#endif	/* _LINUX_SRCU_H_ */

File Added: src/sys/external/bsd/common/linux/linux_srcu.c
/*	$NetBSD: linux_srcu.c,v 1.1 2021/12/19 01:37:27 riastradh Exp $	*/

/*-
 * Copyright (c) 2018 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Taylor R. Campbell.
 *
 * 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, 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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 <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: linux_srcu.c,v 1.1 2021/12/19 01:37:27 riastradh Exp $");

/*
 * SRCU: Sleepable RCU
 *
 *	(This is not exactly SRCU as Linux implements it; it is my
 *	approximation of the semantics I think we need.)
 *
 *	For each srcu context, representing a related set of read
 *	sections, on each CPU we store two counts of numbers of
 *	readers in two epochs: active readers and draining readers.
 *
 *	All new srcu read sections get counted in the active epoch.
 *	When there's no synchronize_srcu in progress, the draining
 *	epoch has zero readers.  When a thread calls synchronize_srcu,
 *	which must be serialized by the caller, it it swaps the sense
 *	of the epochs, issues an xcall to collect a global count of the
 *	number of readers in the now-draining epoch, and waits for the
 *	remainder to complete.
 *
 *	This is basically NetBSD localcount(9), but without the
 *	restriction that the caller of localcount_drain must guarantee
 *	no new readers -- srcu uses two counts per CPU instead of one
 *	like localcount(9), and synchronize_srcu just waits for all
 *	existing readers to drain while new oness count toward a new
 *	epoch.
 */

#include <sys/types.h>
#include <sys/condvar.h>
#include <sys/mutex.h>
#include <sys/percpu.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/xcall.h>

#include <linux/srcu.h>

struct srcu_cpu {
	int64_t	src_count[2];
};

/*
 * srcu_init(srcu, name)
 *
 *	Initialize the srcu state with the specified name.  Caller must
 *	call srcu_fini when done.
 *
 *	name should be no longer than 8 characters; longer will be
 *	truncated.
 *
 *	May sleep.
 */
void
srcu_init(struct srcu *srcu, const char *name)
{

	ASSERT_SLEEPABLE();

	srcu->srcu_percpu = percpu_alloc(sizeof(struct srcu_cpu));
	mutex_init(&srcu->srcu_lock, MUTEX_DEFAULT, IPL_VM);
	cv_init(&srcu->srcu_cv, name);
	srcu->srcu_sync = NULL;
	srcu->srcu_total = 0;
	srcu->srcu_gen = 0;
}

/*
 * srcu_fini(srcu)
 *
 *	Finalize an srcu state, which must not be in use right now.  If
 *	any srcu read sections might be active, caller must wait for
 *	them to complete with synchronize_srcu.
 *
 *	May sleep.
 */
void
srcu_fini(struct srcu *srcu)
{

	ASSERT_SLEEPABLE();

	KASSERTMSG((srcu->srcu_sync == NULL),
	    "srcu_fini in lwp %p while synchronize_srcu running in lwp %p",
	    curlwp, srcu->srcu_sync);
	cv_destroy(&srcu->srcu_cv);
	mutex_destroy(&srcu->srcu_lock);
	percpu_free(srcu->srcu_percpu, sizeof(struct srcu_cpu));
}

/*
 * srcu_adjust(srcu, gen, delta)
 *
 *	Internal subroutine: Add delta to the local CPU's count of
 *	readers in the generation gen.
 *
 *	Never sleeps.
 */
static void
srcu_adjust(struct srcu *srcu, unsigned gen, int delta)
{
	struct srcu_cpu *cpu;
	unsigned epoch = gen & 1; /* active epoch */

	cpu = percpu_getref(srcu->srcu_percpu);
	cpu->src_count[epoch] += delta;
	percpu_putref(srcu->srcu_percpu);
}

/*
 * srcu_read_lock(srcu)
 *
 *	Enter an srcu read section and return a ticket for it.  Any
 *	subsequent synchronize_srcu will wait until this thread calls
 *	srcu_read_unlock(srcu, ticket).
 *
 *	Never sleeps.
 */
int
srcu_read_lock(struct srcu *srcu)
{
	unsigned gen;

	/*
	 * Prevent xcall while we fetch the generation and adjust the
	 * count.
	 */
	kpreempt_disable();
	gen = srcu->srcu_gen;
	/* Fetch the generation once before incrementing the count.  */
	__insn_barrier();
	srcu_adjust(srcu, gen, +1);
	kpreempt_enable();

	/*
	 * Increment the count in our generation before doing anything
	 * else on this CPU.
	 *
	 * No stronger, inter-CPU memory barrier is needed: if there is
	 * a concurrent synchronize_srcu, it will issue an xcall that
	 * functions as a stronger memory barrier.
	 */
	__insn_barrier();

	return gen;
}

/*
 * srcu_read_unlock(srcu, ticket)
 *
 *	Exit an srcu read section started with srcu_read_lock returning
 *	ticket.  If there is a pending synchronize_srcu and we might be
 *	the last reader, notify it.
 *
 *	Never sleeps.
 */
void
srcu_read_unlock(struct srcu *srcu, int ticket)
{
	unsigned gen = ticket;

	/*
	 * Make sure all side effects have completed on this CPU before
	 * decrementing the count.
	 *
	 * No stronger, inter-CPU memory barrier is needed: if there is
	 * a concurrent synchronize_srcu, it will issue an xcall that
	 * functions as a stronger memory barrier.
	 */
	__insn_barrier();

	/*
	 * Prevent xcall while we determine whether we need to notify a
	 * sync and decrement the count in our generation.
	 */
	kpreempt_disable();
	if (__predict_true(gen == srcu->srcu_gen)) {
		/*
		 * Fast path: just decrement the local count.  If a
		 * sync has begun and incremented gen after we observed
		 * it, it will issue an xcall that will run after this
		 * kpreempt_disable section to collect our local count.
		 */
		srcu_adjust(srcu, gen, -1);
	} else {
		/*
		 * Slow path: decrement the total count, and if it goes
		 * to zero, notify the sync in progress.  The xcall may
		 * have already run, or it may have yet to run; since
		 * we can't tell which, we must contribute to the
		 * global count, not to our local count.
		 */
		mutex_spin_enter(&srcu->srcu_lock);
		KASSERT(srcu->srcu_sync != NULL);
		if (--srcu->srcu_total == 0)
			cv_broadcast(&srcu->srcu_cv);
		mutex_spin_exit(&srcu->srcu_lock);
	}
	kpreempt_enable();
}

/*
 * synchronize_srcu_xc(a, b)
 *
 *	Cross-call function for synchronize_srcu: a is the struct srcu
 *	pointer; b is ignored.  Transfer the local count of srcu
 *	readers on this CPU in the inactive epoch to the global count
 *	under the srcu sync lock.
 */
static void
synchronize_srcu_xc(void *a, void *b)
{
	struct srcu *srcu = a;
	struct srcu_cpu *cpu;
	unsigned gen, epoch;
	uint64_t local;

	/* Operate under the sync lock.  Blocks preemption as side effect.  */
	mutex_spin_enter(&srcu->srcu_lock);

	gen = srcu->srcu_gen;	/* active generation */
	epoch = 1 ^ (gen & 1);	/* draining epoch */

	/* Transfer the local count to the global count.  */
	cpu = percpu_getref(srcu->srcu_percpu);
	local = cpu->src_count[epoch];
	srcu->srcu_total += local;
	cpu->src_count[epoch] -= local; /* i.e., cpu->src_count[epoch] = 0 */
	KASSERT(cpu->src_count[epoch] == 0);
	percpu_putref(srcu->srcu_percpu);

	mutex_spin_exit(&srcu->srcu_lock);
}

/*
 * synchronize_srcu(srcu)
 *
 *	Wait for all srcu readers on all CPUs that may have begun
 *	before sychronize_srcu to complete.
 *
 *	May sleep.  (Practically guaranteed to sleep!)
 */
void
synchronize_srcu(struct srcu *srcu)
{

	ASSERT_SLEEPABLE();

	/* Start a sync, and advance the active generation.  */
	mutex_spin_enter(&srcu->srcu_lock);
	while (srcu->srcu_sync != NULL)
		cv_wait(&srcu->srcu_cv, &srcu->srcu_lock);
	KASSERT(srcu->srcu_total == 0);
	srcu->srcu_sync = curlwp;
	srcu->srcu_gen++;
	mutex_spin_exit(&srcu->srcu_lock);

	/*
	 * Wait for all CPUs to witness the change to the active
	 * generation, and collect their local counts in the draining
	 * epoch into the global count.
	 */
	xc_wait(xc_broadcast(0, synchronize_srcu_xc, srcu, NULL));

	/*
	 * Wait for the global count of users in the draining epoch to
	 * drain to zero.
	 */
	mutex_spin_enter(&srcu->srcu_lock);
	while (srcu->srcu_total != 0)
		cv_wait(&srcu->srcu_cv, &srcu->srcu_lock);
	srcu->srcu_sync = NULL;
	cv_broadcast(&srcu->srcu_cv);
	mutex_spin_exit(&srcu->srcu_lock);
}

File Deleted: src/sys/external/bsd/drm2/include/linux/Attic/srcu.h

cvs diff -r1.22 -r1.23 src/sys/external/bsd/drm2/linux/files.drmkms_linux (expand / switch to unified diff)

--- src/sys/external/bsd/drm2/linux/files.drmkms_linux 2021/12/19 01:34:57 1.22
+++ src/sys/external/bsd/drm2/linux/files.drmkms_linux 2021/12/19 01:37:28 1.23
@@ -1,23 +1,22 @@ @@ -1,23 +1,22 @@
1# $NetBSD: files.drmkms_linux,v 1.22 2021/12/19 01:34:57 riastradh Exp $ 1# $NetBSD: files.drmkms_linux,v 1.23 2021/12/19 01:37:28 riastradh Exp $
2 2
3define drmkms_linux: i2cexec, i2c_bitbang 3define drmkms_linux: i2cexec, i2c_bitbang
4 4
5makeoptions drmkms_linux CPPFLAGS+="-I$S/external/bsd/common/include" 5makeoptions drmkms_linux CPPFLAGS+="-I$S/external/bsd/common/include"
6makeoptions drmkms_linux CPPFLAGS+="-I$S/external/bsd/drm2/include" 6makeoptions drmkms_linux CPPFLAGS+="-I$S/external/bsd/drm2/include"
7 7
8file external/bsd/drm2/linux/linux_atomic64.c drmkms_linux 8file external/bsd/drm2/linux/linux_atomic64.c drmkms_linux
9file external/bsd/drm2/linux/linux_dma_buf.c drmkms_linux 9file external/bsd/drm2/linux/linux_dma_buf.c drmkms_linux
10file external/bsd/drm2/linux/linux_dma_fence.c drmkms_linux 10file external/bsd/drm2/linux/linux_dma_fence.c drmkms_linux
11file external/bsd/drm2/linux/linux_dmi.c drmkms_linux 11file external/bsd/drm2/linux/linux_dmi.c drmkms_linux
12file external/bsd/drm2/linux/linux_i2c.c drmkms_linux 12file external/bsd/drm2/linux/linux_i2c.c drmkms_linux
13file external/bsd/drm2/linux/linux_idr.c drmkms_linux 13file external/bsd/drm2/linux/linux_idr.c drmkms_linux
14file external/bsd/drm2/linux/linux_kmap.c drmkms_linux 14file external/bsd/drm2/linux/linux_kmap.c drmkms_linux
15file external/bsd/drm2/linux/linux_list_sort.c drmkms_linux 15file external/bsd/drm2/linux/linux_list_sort.c drmkms_linux
16file external/bsd/drm2/linux/linux_module.c drmkms_linux 16file external/bsd/drm2/linux/linux_module.c drmkms_linux
17file external/bsd/drm2/linux/linux_pci.c drmkms_linux 17file external/bsd/drm2/linux/linux_pci.c drmkms_linux
18file external/bsd/drm2/linux/linux_reservation.c drmkms_linux 18file external/bsd/drm2/linux/linux_reservation.c drmkms_linux
19file external/bsd/drm2/linux/linux_srcu.c drmkms_linux 
20file external/bsd/drm2/linux/linux_stop_machine.c drmkms_linux 19file external/bsd/drm2/linux/linux_stop_machine.c drmkms_linux
21file external/bsd/drm2/linux/linux_wait_bit.c drmkms_linux 20file external/bsd/drm2/linux/linux_wait_bit.c drmkms_linux
22file external/bsd/drm2/linux/linux_writecomb.c drmkms_linux 21file external/bsd/drm2/linux/linux_writecomb.c drmkms_linux
23file external/bsd/drm2/linux/linux_ww_mutex.c drmkms_linux 22file external/bsd/drm2/linux/linux_ww_mutex.c drmkms_linux

File Deleted: src/sys/external/bsd/drm2/linux/Attic/linux_srcu.c