1/* 2 * rcuref.h 3 * 4 * Reference counting for elements of lists/arrays protected by 5 * RCU. 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 * 21 * Copyright (C) IBM Corporation, 2005 22 * 23 * Author: Dipankar Sarma <dipankar@in.ibm.com> 24 * Ravikiran Thirumalai <kiran_th@gmail.com> 25 * 26 * See Documentation/RCU/rcuref.txt for detailed user guide. 27 * 28 */ 29 30#ifndef _RCUREF_H_ 31#define _RCUREF_H_ 32 33#ifdef __KERNEL__ 34 35#include <linux/types.h> 36#include <linux/interrupt.h> 37#include <linux/spinlock.h> 38#include <asm/atomic.h> 39 40/* 41 * These APIs work on traditional atomic_t counters used in the 42 * kernel for reference counting. Under special circumstances 43 * where a lock-free get() operation races with a put() operation 44 * these APIs can be used. See Documentation/RCU/rcuref.txt. 45 */ 46 47#ifdef __HAVE_ARCH_CMPXCHG 48 49/** 50 * rcuref_inc - increment refcount for object. 51 * @rcuref: reference counter in the object in question. 52 * 53 * This should be used only for objects where we use RCU and 54 * use the rcuref_inc_lf() api to acquire a reference 55 * in a lock-free reader-side critical section. 56 */ 57static inline void rcuref_inc(atomic_t *rcuref) 58{ 59 atomic_inc(rcuref); 60} 61 62/** 63 * rcuref_dec - decrement refcount for object. 64 * @rcuref: reference counter in the object in question. 65 * 66 * This should be used only for objects where we use RCU and 67 * use the rcuref_inc_lf() api to acquire a reference 68 * in a lock-free reader-side critical section. 69 */ 70static inline void rcuref_dec(atomic_t *rcuref) 71{ 72 atomic_dec(rcuref); 73} 74 75/** 76 * rcuref_dec_and_test - decrement refcount for object and test 77 * @rcuref: reference counter in the object. 78 * @release: pointer to the function that will clean up the object 79 * when the last reference to the object is released. 80 * This pointer is required. 81 * 82 * Decrement the refcount, and if 0, return 1. Else return 0. 83 * 84 * This should be used only for objects where we use RCU and 85 * use the rcuref_inc_lf() api to acquire a reference 86 * in a lock-free reader-side critical section. 87 */ 88static inline int rcuref_dec_and_test(atomic_t *rcuref) 89{ 90 return atomic_dec_and_test(rcuref); 91} 92 93/* 94 * cmpxchg is needed on UP too, if deletions to the list/array can happen 95 * in interrupt context. 96 */ 97 98/** 99 * rcuref_inc_lf - Take reference to an object in a read-side 100 * critical section protected by RCU. 101 * @rcuref: reference counter in the object in question. 102 * 103 * Try and increment the refcount by 1. The increment might fail if 104 * the reference counter has been through a 1 to 0 transition and 105 * is no longer part of the lock-free list. 106 * Returns non-zero on successful increment and zero otherwise. 107 */ 108static inline int rcuref_inc_lf(atomic_t *rcuref) 109{ 110 int c, old; 111 c = atomic_read(rcuref); 112 while (c && (old = cmpxchg(&rcuref->counter, c, c + 1)) != c) 113 c = old; 114 return c; 115} 116 117#else /* !__HAVE_ARCH_CMPXCHG */ 118 119extern spinlock_t __rcuref_hash[]; 120 121/* 122 * Use a hash table of locks to protect the reference count 123 * since cmpxchg is not available in this arch. 124 */ 125#ifdef CONFIG_SMP 126#define RCUREF_HASH_SIZE 4 127#define RCUREF_HASH(k) \ 128 (&__rcuref_hash[(((unsigned long)k)>>8) & (RCUREF_HASH_SIZE-1)]) 129#else 130#define RCUREF_HASH_SIZE 1 131#define RCUREF_HASH(k) &__rcuref_hash[0] 132#endif /* CONFIG_SMP */ 133 134/** 135 * rcuref_inc - increment refcount for object. 136 * @rcuref: reference counter in the object in question. 137 * 138 * This should be used only for objects where we use RCU and 139 * use the rcuref_inc_lf() api to acquire a reference in a lock-free 140 * reader-side critical section. 141 */ 142static inline void rcuref_inc(atomic_t *rcuref) 143{ 144 unsigned long flags; 145 spin_lock_irqsave(RCUREF_HASH(rcuref), flags); 146 rcuref->counter += 1; 147 spin_unlock_irqrestore(RCUREF_HASH(rcuref), flags); 148} 149 150/** 151 * rcuref_dec - decrement refcount for object. 152 * @rcuref: reference counter in the object in question. 153 * 154 * This should be used only for objects where we use RCU and 155 * use the rcuref_inc_lf() api to acquire a reference in a lock-free 156 * reader-side critical section. 157 */ 158static inline void rcuref_dec(atomic_t *rcuref) 159{ 160 unsigned long flags; 161 spin_lock_irqsave(RCUREF_HASH(rcuref), flags); 162 rcuref->counter -= 1; 163 spin_unlock_irqrestore(RCUREF_HASH(rcuref), flags); 164} 165 166/** 167 * rcuref_dec_and_test - decrement refcount for object and test 168 * @rcuref: reference counter in the object. 169 * @release: pointer to the function that will clean up the object 170 * when the last reference to the object is released. 171 * This pointer is required. 172 * 173 * Decrement the refcount, and if 0, return 1. Else return 0. 174 * 175 * This should be used only for objects where we use RCU and 176 * use the rcuref_inc_lf() api to acquire a reference in a lock-free 177 * reader-side critical section. 178 */ 179static inline int rcuref_dec_and_test(atomic_t *rcuref) 180{ 181 unsigned long flags; 182 spin_lock_irqsave(RCUREF_HASH(rcuref), flags); 183 rcuref->counter--; 184 if (!rcuref->counter) { 185 spin_unlock_irqrestore(RCUREF_HASH(rcuref), flags); 186 return 1; 187 } else { 188 spin_unlock_irqrestore(RCUREF_HASH(rcuref), flags); 189 return 0; 190 } 191} 192 193/** 194 * rcuref_inc_lf - Take reference to an object of a lock-free collection 195 * by traversing a lock-free list/array. 196 * @rcuref: reference counter in the object in question. 197 * 198 * Try and increment the refcount by 1. The increment might fail if 199 * the reference counter has been through a 1 to 0 transition and 200 * object is no longer part of the lock-free list. 201 * Returns non-zero on successful increment and zero otherwise. 202 */ 203static inline int rcuref_inc_lf(atomic_t *rcuref) 204{ 205 int ret; 206 unsigned long flags; 207 spin_lock_irqsave(RCUREF_HASH(rcuref), flags); 208 if (rcuref->counter) 209 ret = rcuref->counter++; 210 else 211 ret = 0; 212 spin_unlock_irqrestore(RCUREF_HASH(rcuref), flags); 213 return ret; 214} 215 216 217#endif /* !__HAVE_ARCH_CMPXCHG */ 218 219#endif /* __KERNEL__ */ 220#endif /* _RCUREF_H_ */