1/*- 2 * Copyright (c) 2014 Mateusz Guzik <mjg@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 * $FreeBSD$ 26 */ 27 28#ifndef _SYS_SEQ_H_ 29#define _SYS_SEQ_H_ 30 31#ifdef _KERNEL 32#include <sys/systm.h> 33#endif 34#include <sys/types.h> 35 36/* 37 * seq_t may be included in structs visible to userspace 38 */ 39typedef uint32_t seq_t; 40 41#ifdef _KERNEL 42 43/* 44 * Typical usage: 45 * 46 * writers: 47 * lock_exclusive(&obj->lock); 48 * seq_write_begin(&obj->seq); 49 * ..... 50 * seq_write_end(&obj->seq); 51 * unlock_exclusive(&obj->unlock); 52 * 53 * readers: 54 * obj_t lobj; 55 * seq_t seq; 56 * 57 * for (;;) { 58 * seq = seq_read(&gobj->seq); 59 * lobj = gobj; 60 * if (seq_consistent(&gobj->seq, seq)) 61 * break; 62 * cpu_spinwait(); 63 * } 64 * foo(lobj); 65 */ 66 67/* A hack to get MPASS macro */ 68#include <sys/lock.h> 69 70#include <machine/cpu.h> 71 72/* 73 * This is a temporary hack until memory barriers are cleaned up. 74 * 75 * atomic_load_acq_int at least on amd64 provides a full memory barrier, 76 * in a way which affects perforance. 77 * 78 * Hack below covers all architectures and avoids most of the penalty at least 79 * on amd64. 80 */ 81static __inline int 82atomic_load_acq_rmb_int(volatile u_int *p) 83{ 84 volatile u_int v; 85 86 v = *p; 87 atomic_load_acq_int(&v); 88 return (v); 89} 90 91static __inline bool 92seq_in_modify(seq_t seqp) 93{ 94 95 return (seqp & 1); 96} 97 98static __inline void 99seq_write_begin(seq_t *seqp) 100{ 101 102 MPASS(!seq_in_modify(*seqp)); 103 atomic_add_acq_int(seqp, 1); 104} 105 106static __inline void 107seq_write_end(seq_t *seqp) 108{ 109 110 atomic_add_rel_int(seqp, 1); 111 MPASS(!seq_in_modify(*seqp)); 112} 113 114static __inline seq_t 115seq_read(seq_t *seqp) 116{ 117 seq_t ret; 118 119 for (;;) { 120 ret = atomic_load_acq_rmb_int(seqp); 121 if (seq_in_modify(ret)) { 122 cpu_spinwait(); 123 continue; 124 } 125 break; 126 } 127 128 return (ret); 129} 130 131static __inline seq_t 132seq_consistent(seq_t *seqp, seq_t oldseq) 133{ 134 135 return (atomic_load_acq_rmb_int(seqp) == oldseq); 136} 137 138static __inline seq_t 139seq_consistent_nomb(seq_t *seqp, seq_t oldseq) 140{ 141 142 return (*seqp == oldseq); 143} 144 145#endif /* _KERNEL */ 146#endif /* _SYS_SEQ_H_ */ 147