1/* 2 * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. 3 * Copyright (C) 2007 The Regents of the University of California. 4 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). 5 * Written by Brian Behlendorf <behlendorf1@llnl.gov>. 6 * UCRL-CODE-235197 7 * 8 * This file is part of the SPL, Solaris Porting Layer. 9 * 10 * The SPL is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License as published by the 12 * Free Software Foundation; either version 2 of the License, or (at your 13 * option) any later version. 14 * 15 * The SPL is distributed in the hope that it will be useful, but WITHOUT 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 18 * for more details. 19 * 20 * You should have received a copy of the GNU General Public License along 21 * with the SPL. If not, see <http://www.gnu.org/licenses/>. 22 */ 23 24#ifndef _SPL_RWLOCK_H 25#define _SPL_RWLOCK_H 26 27#include <sys/types.h> 28#include <linux/rwsem.h> 29#include <linux/sched.h> 30 31typedef enum { 32 RW_DRIVER = 2, 33 RW_DEFAULT = 4, 34 RW_NOLOCKDEP = 5 35} krw_type_t; 36 37typedef enum { 38 RW_NONE = 0, 39 RW_WRITER = 1, 40 RW_READER = 2 41} krw_t; 42 43typedef struct { 44 struct rw_semaphore rw_rwlock; 45 kthread_t *rw_owner; 46#ifdef CONFIG_LOCKDEP 47 krw_type_t rw_type; 48#endif /* CONFIG_LOCKDEP */ 49} krwlock_t; 50 51#define SEM(rwp) (&(rwp)->rw_rwlock) 52 53static inline void 54spl_rw_set_owner(krwlock_t *rwp) 55{ 56 rwp->rw_owner = current; 57} 58 59static inline void 60spl_rw_clear_owner(krwlock_t *rwp) 61{ 62 rwp->rw_owner = NULL; 63} 64 65static inline kthread_t * 66rw_owner(krwlock_t *rwp) 67{ 68 return (rwp->rw_owner); 69} 70 71#ifdef CONFIG_LOCKDEP 72static inline void 73spl_rw_set_type(krwlock_t *rwp, krw_type_t type) 74{ 75 rwp->rw_type = type; 76} 77static inline void 78spl_rw_lockdep_off_maybe(krwlock_t *rwp) \ 79{ \ 80 if (rwp && rwp->rw_type == RW_NOLOCKDEP) \ 81 lockdep_off(); \ 82} 83static inline void 84spl_rw_lockdep_on_maybe(krwlock_t *rwp) \ 85{ \ 86 if (rwp && rwp->rw_type == RW_NOLOCKDEP) \ 87 lockdep_on(); \ 88} 89#else /* CONFIG_LOCKDEP */ 90#define spl_rw_set_type(rwp, type) 91#define spl_rw_lockdep_off_maybe(rwp) 92#define spl_rw_lockdep_on_maybe(rwp) 93#endif /* CONFIG_LOCKDEP */ 94 95static inline int 96RW_LOCK_HELD(krwlock_t *rwp) 97{ 98 return (rwsem_is_locked(SEM(rwp))); 99} 100 101static inline int 102RW_WRITE_HELD(krwlock_t *rwp) 103{ 104 return (rw_owner(rwp) == current); 105} 106 107static inline int 108RW_READ_HELD(krwlock_t *rwp) 109{ 110 return (RW_LOCK_HELD(rwp) && rw_owner(rwp) == NULL); 111} 112 113/* 114 * The following functions must be a #define and not static inline. 115 * This ensures that the native linux semaphore functions (down/up) 116 * will be correctly located in the users code which is important 117 * for the built in kernel lock analysis tools 118 */ 119#define rw_init(rwp, name, type, arg) /* CSTYLED */ \ 120({ \ 121 static struct lock_class_key __key; \ 122 ASSERT(type == RW_DEFAULT || type == RW_NOLOCKDEP); \ 123 \ 124 __init_rwsem(SEM(rwp), #rwp, &__key); \ 125 spl_rw_clear_owner(rwp); \ 126 spl_rw_set_type(rwp, type); \ 127}) 128 129/* 130 * The Linux rwsem implementation does not require a matching destroy. 131 */ 132#define rw_destroy(rwp) ((void) 0) 133 134/* 135 * Upgrading a rwsem from a reader to a writer is not supported by the 136 * Linux kernel. The lock must be dropped and reacquired as a writer. 137 */ 138#define rw_tryupgrade(rwp) RW_WRITE_HELD(rwp) 139 140#define rw_tryenter(rwp, rw) /* CSTYLED */ \ 141({ \ 142 int _rc_ = 0; \ 143 \ 144 spl_rw_lockdep_off_maybe(rwp); \ 145 switch (rw) { \ 146 case RW_READER: \ 147 _rc_ = down_read_trylock(SEM(rwp)); \ 148 break; \ 149 case RW_WRITER: \ 150 if ((_rc_ = down_write_trylock(SEM(rwp)))) \ 151 spl_rw_set_owner(rwp); \ 152 break; \ 153 default: \ 154 VERIFY(0); \ 155 } \ 156 spl_rw_lockdep_on_maybe(rwp); \ 157 _rc_; \ 158}) 159 160#define rw_enter(rwp, rw) /* CSTYLED */ \ 161({ \ 162 spl_rw_lockdep_off_maybe(rwp); \ 163 switch (rw) { \ 164 case RW_READER: \ 165 down_read(SEM(rwp)); \ 166 break; \ 167 case RW_WRITER: \ 168 down_write(SEM(rwp)); \ 169 spl_rw_set_owner(rwp); \ 170 break; \ 171 default: \ 172 VERIFY(0); \ 173 } \ 174 spl_rw_lockdep_on_maybe(rwp); \ 175}) 176 177#define rw_exit(rwp) /* CSTYLED */ \ 178({ \ 179 spl_rw_lockdep_off_maybe(rwp); \ 180 if (RW_WRITE_HELD(rwp)) { \ 181 spl_rw_clear_owner(rwp); \ 182 up_write(SEM(rwp)); \ 183 } else { \ 184 ASSERT(RW_READ_HELD(rwp)); \ 185 up_read(SEM(rwp)); \ 186 } \ 187 spl_rw_lockdep_on_maybe(rwp); \ 188}) 189 190#define rw_downgrade(rwp) /* CSTYLED */ \ 191({ \ 192 spl_rw_lockdep_off_maybe(rwp); \ 193 spl_rw_clear_owner(rwp); \ 194 downgrade_write(SEM(rwp)); \ 195 spl_rw_lockdep_on_maybe(rwp); \ 196}) 197 198#endif /* _SPL_RWLOCK_H */ 199