diff --git a/sys/compat/linuxkpi/common/include/linux/seqlock.h b/sys/compat/linuxkpi/common/include/linux/seqlock.h index 6e81e7a0fa45..4a5385f5e095 100644 --- a/sys/compat/linuxkpi/common/include/linux/seqlock.h +++ b/sys/compat/linuxkpi/common/include/linux/seqlock.h @@ -1,161 +1,232 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2021 Vladimir Kondratyev * * 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 AUTHOR 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 AUTHOR 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 _LINUXKPI_LINUX_SEQLOCK_H__ #define _LINUXKPI_LINUX_SEQLOCK_H__ #include #include #include #include +#include #include +#include + struct lock_class_key; struct seqcount { seqc_t seqc; }; typedef struct seqcount seqcount_t; struct seqlock { struct mtx seql_lock; struct seqcount seql_count; }; typedef struct seqlock seqlock_t; +struct seqcount_mutex { + struct mutex *seqm_lock; + struct seqcount seqm_count; +}; +typedef struct seqcount_mutex seqcount_mutex_t; + static inline void __seqcount_init(struct seqcount *seqcount, const char *name __unused, struct lock_class_key *key __unused) { seqcount->seqc = 0; } #define seqcount_init(seqcount) __seqcount_init(seqcount, NULL, NULL) static inline void -write_seqcount_begin(struct seqcount *seqcount) +seqcount_mutex_init(struct seqcount_mutex *seqcount, struct mutex *mutex) +{ + seqcount->seqm_lock = mutex; + seqcount_init(&seqcount->seqm_count); +} + +#define write_seqcount_begin(s) \ + _Generic(*(s), \ + struct seqcount: lkpi_write_seqcount_begin, \ + struct seqcount_mutex: lkpi_write_seqcount_mutex_begin \ + )(s) + +static inline void +lkpi_write_seqcount_begin(struct seqcount *seqcount) { seqc_sleepable_write_begin(&seqcount->seqc); } static inline void -write_seqcount_end(struct seqcount *seqcount) +lkpi_write_seqcount_mutex_begin(struct seqcount_mutex *seqcount) +{ + mutex_lock(seqcount->seqm_lock); + lkpi_write_seqcount_begin(&seqcount->seqm_count); +} + +#define write_seqcount_end(s) \ + _Generic(*(s), \ + struct seqcount: lkpi_write_seqcount_end, \ + struct seqcount_mutex: lkpi_write_seqcount_mutex_end \ + )(s) + +static inline void +lkpi_write_seqcount_end(struct seqcount *seqcount) { seqc_sleepable_write_end(&seqcount->seqc); } -/* - * XXX: Are predicts from inline functions still not honored by clang? - */ -#define __read_seqcount_retry(seqcount, gen) \ - (!seqc_consistent_no_fence(&(seqcount)->seqc, gen)) -#define read_seqcount_retry(seqcount, gen) \ - (!seqc_consistent(&(seqcount)->seqc, gen)) +static inline void +lkpi_write_seqcount_mutex_end(struct seqcount_mutex *seqcount) +{ + lkpi_write_seqcount_end(&seqcount->seqm_count); + mutex_unlock(seqcount->seqm_lock); +} + +#define read_seqcount_begin(s) \ + _Generic(*(s), \ + struct seqcount: lkpi_read_seqcount_begin, \ + struct seqcount_mutex: lkpi_read_seqcount_mutex_begin \ + )(s) static inline unsigned -read_seqcount_begin(const struct seqcount *seqcount) +lkpi_read_seqcount_begin(const struct seqcount *seqcount) { return (seqc_read(&seqcount->seqc)); } +static inline unsigned +lkpi_read_seqcount_mutex_begin(const struct seqcount_mutex *seqcount) +{ + return (lkpi_read_seqcount_begin(&seqcount->seqm_count)); +} + static inline unsigned raw_read_seqcount(const struct seqcount *seqcount) { return (seqc_read_any(&seqcount->seqc)); } +/* + * XXX: Are predicts from inline functions still not honored by clang? + */ +#define __read_seqcount_retry(seqcount, gen) \ + (!seqc_consistent_no_fence(&(seqcount)->seqc, gen)) +#define read_seqcount_retry(s, old) \ + _Generic(*(s), \ + struct seqcount: lkpi_read_seqcount_retry, \ + struct seqcount_mutex: lkpi_read_seqcount_mutex_retry \ + )(s, old) + +static inline int +lkpi_read_seqcount_retry( + const struct seqcount *seqcount, unsigned int old) +{ + return (!seqc_consistent(&seqcount->seqc, old)); +} + +static inline int +lkpi_read_seqcount_mutex_retry( + const struct seqcount_mutex *seqcount, unsigned int old) +{ + return (!seqc_consistent(&seqcount->seqm_count.seqc, old)); +} + static inline void seqlock_init(struct seqlock *seqlock) { /* * Don't enroll to witness(4) to avoid orphaned references after struct * seqlock has been freed. There is no seqlock destructor exists so we * can't expect automatic mtx_destroy() execution before free(). */ mtx_init(&seqlock->seql_lock, "seqlock", NULL, MTX_DEF|MTX_NOWITNESS); seqcount_init(&seqlock->seql_count); } static inline void lkpi_write_seqlock(struct seqlock *seqlock, const bool irqsave) { mtx_lock(&seqlock->seql_lock); if (irqsave) critical_enter(); write_seqcount_begin(&seqlock->seql_count); } static inline void write_seqlock(struct seqlock *seqlock) { lkpi_write_seqlock(seqlock, false); } static inline void lkpi_write_sequnlock(struct seqlock *seqlock, const bool irqsave) { write_seqcount_end(&seqlock->seql_count); if (irqsave) critical_exit(); mtx_unlock(&seqlock->seql_lock); } static inline void write_sequnlock(struct seqlock *seqlock) { lkpi_write_sequnlock(seqlock, false); } /* * Disable preemption when the consumer wants to disable interrupts. This * ensures that the caller won't be starved if it is preempted by a * higher-priority reader, but assumes that the caller won't perform any * blocking operations while holding the write lock; probably a safe * assumption. */ #define write_seqlock_irqsave(seqlock, flags) do { \ (flags) = 0; \ lkpi_write_seqlock(seqlock, true); \ } while (0) static inline void write_sequnlock_irqrestore(struct seqlock *seqlock, unsigned long flags __unused) { lkpi_write_sequnlock(seqlock, true); } static inline unsigned read_seqbegin(const struct seqlock *seqlock) { return (read_seqcount_begin(&seqlock->seql_count)); } #define read_seqretry(seqlock, gen) \ read_seqcount_retry(&(seqlock)->seql_count, gen) #endif /* _LINUXKPI_LINUX_SEQLOCK_H__ */