]> git.proxmox.com Git - mirror_spl-debian.git/blob - include/linux-rwlock.h
a6a2787d8af65834b3c18c8babcf7216b473a277
[mirror_spl-debian.git] / include / linux-rwlock.h
1 #ifndef _SYS_LINUX_RWLOCK_H
2 #define _SYS_LINUX_RWLOCK_H
3
4 #include <linux/slab.h>
5 #include <linux/rwsem.h>
6 #include <asm/current.h>
7 #include <sys/linux-types.h>
8
9 #ifdef __cplusplus
10 extern "C" {
11 #endif
12
13 typedef enum {
14 RW_DRIVER = 2, /* driver (DDI) rwlock */
15 RW_DEFAULT = 4 /* kernel default rwlock */
16 } krw_type_t;
17
18 typedef enum {
19 RW_WRITER,
20 RW_READER
21 } krw_t;
22
23 #define RW_READ_HELD(x) (rw_read_held((x)))
24 #define RW_WRITE_HELD(x) (rw_write_held((x)))
25 #define RW_LOCK_HELD(x) (rw_lock_held((x)))
26 #define RW_ISWRITER(x) (rw_iswriter(x))
27
28 #define RW_MAGIC 0x3423645a
29 #define RW_POISON 0xa6
30
31 typedef struct {
32 int rw_magic;
33 char *rw_name;
34 struct rw_semaphore rw_sem;
35 struct task_struct *rw_owner; /* holder of the write lock */
36 } krwlock_t;
37
38 static __inline__ void
39 rw_init(krwlock_t *rwlp, char *name, krw_type_t type, void *arg)
40 {
41 BUG_ON(type != RW_DEFAULT); /* XXX no irq handler use */
42 BUG_ON(arg != NULL); /* XXX no irq handler use */
43 rwlp->rw_magic = RW_MAGIC;
44 rwlp->rw_owner = NULL; /* no one holds the write lock yet */
45 init_rwsem(&rwlp->rw_sem);
46 rwlp->rw_name = NULL;
47
48 if (name) {
49 rwlp->rw_name = kmalloc(strlen(name) + 1, GFP_KERNEL);
50 if (rwlp->rw_name)
51 strcpy(rwlp->rw_name, name);
52 }
53 }
54
55 static __inline__ void
56 rw_destroy(krwlock_t *rwlp)
57 {
58 BUG_ON(rwlp == NULL);
59 BUG_ON(rwlp->rw_magic != RW_MAGIC);
60 BUG_ON(rwlp->rw_owner != NULL);
61 spin_lock(&rwlp->rw_sem.wait_lock);
62 BUG_ON(!list_empty(&rwlp->rw_sem.wait_list));
63 spin_unlock(&rwlp->rw_sem.wait_lock);
64
65 if (rwlp->rw_name)
66 kfree(rwlp->rw_name);
67
68 memset(rwlp, RW_POISON, sizeof(krwlock_t));
69 }
70
71 /* Return 0 if the lock could not be obtained without blocking.
72 */
73 static __inline__ int
74 rw_tryenter(krwlock_t *rwlp, krw_t rw)
75 {
76 int result;
77
78 BUG_ON(rwlp->rw_magic != RW_MAGIC);
79 switch (rw) {
80 /* these functions return 1 if success, 0 if contention */
81 case RW_READER:
82 /* Here the Solaris code would return 0
83 * if there were any write waiters. Specifically
84 * thinking about the case where readers may have
85 * the lock and we would also allow this thread
86 * to grab the read lock with a writer waiting in the
87 * queue. This doesn't seem like a correctness
88 * issue, so just call down_read_trylock()
89 * for the test. We may have to revisit this if
90 * it becomes an issue */
91 result = down_read_trylock(&rwlp->rw_sem);
92 break;
93 case RW_WRITER:
94 result = down_write_trylock(&rwlp->rw_sem);
95 if (result) {
96 /* there better not be anyone else
97 * holding the write lock here */
98 BUG_ON(rwlp->rw_owner != NULL);
99 rwlp->rw_owner = current;
100 }
101 break;
102 }
103
104 return result;
105 }
106
107 static __inline__ void
108 rw_enter(krwlock_t *rwlp, krw_t rw)
109 {
110 BUG_ON(rwlp->rw_magic != RW_MAGIC);
111 switch (rw) {
112 case RW_READER:
113 /* Here the Solaris code would block
114 * if there were any write waiters. Specifically
115 * thinking about the case where readers may have
116 * the lock and we would also allow this thread
117 * to grab the read lock with a writer waiting in the
118 * queue. This doesn't seem like a correctness
119 * issue, so just call down_read()
120 * for the test. We may have to revisit this if
121 * it becomes an issue */
122 down_read(&rwlp->rw_sem);
123 break;
124 case RW_WRITER:
125 down_write(&rwlp->rw_sem);
126
127 /* there better not be anyone else
128 * holding the write lock here */
129 BUG_ON(rwlp->rw_owner != NULL);
130 rwlp->rw_owner = current;
131 break;
132 }
133 }
134
135 static __inline__ void
136 rw_exit(krwlock_t *rwlp)
137 {
138 BUG_ON(rwlp->rw_magic != RW_MAGIC);
139
140 /* rw_owner is held by current
141 * thread iff it is a writer */
142 if (rwlp->rw_owner == current) {
143 rwlp->rw_owner = NULL;
144 up_write(&rwlp->rw_sem);
145 } else {
146 up_read(&rwlp->rw_sem);
147 }
148 }
149
150 static __inline__ void
151 rw_downgrade(krwlock_t *rwlp)
152 {
153 BUG_ON(rwlp->rw_magic != RW_MAGIC);
154 BUG_ON(rwlp->rw_owner != current);
155 rwlp->rw_owner = NULL;
156 downgrade_write(&rwlp->rw_sem);
157 }
158
159 /* Return 0 if unable to perform the upgrade.
160 * Might be wise to fix the caller
161 * to acquire the write lock first?
162 */
163 static __inline__ int
164 rw_tryupgrade(krwlock_t *rwlp)
165 {
166 int result;
167 BUG_ON(rwlp->rw_magic != RW_MAGIC);
168
169 spin_lock(&rwlp->rw_sem.wait_lock);
170
171 /* Check if there is anyone waiting for the
172 * lock. If there is, then we know we should
173 * not try to upgrade the lock */
174 if (!list_empty(&rwlp->rw_sem.wait_list)) {
175 printk(KERN_WARNING "There are threads waiting\n");
176 spin_unlock(&rwlp->rw_sem.wait_lock);
177 return 0;
178 }
179 #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
180 /* Note that activity is protected by
181 * the wait_lock. Don't try to upgrade
182 * if there are multiple readers currently
183 * holding the lock */
184 if (rwlp->rw_sem.activity > 1) {
185 #else
186 /* Don't try to upgrade
187 * if there are multiple readers currently
188 * holding the lock */
189 if ((rwlp->rw_sem.count & RWSEM_ACTIVE_MASK) > 1) {
190 #endif
191 spin_unlock(&rwlp->rw_sem.wait_lock);
192 return 0;
193 }
194
195 /* Here it should be safe to drop the
196 * read lock and reacquire it for writing since
197 * we know there are no waiters */
198 up_read(&rwlp->rw_sem);
199
200 /* returns 1 if success, 0 if contention */
201 result = down_write_trylock(&rwlp->rw_sem);
202
203 /* Check if upgrade failed. Should not ever happen
204 * if we got to this point */
205 BUG_ON(!result);
206 BUG_ON(rwlp->rw_owner != NULL);
207 rwlp->rw_owner = current;
208 spin_unlock(&rwlp->rw_sem.wait_lock);
209 return 1;
210 }
211
212 static __inline__ kthread_t *
213 rw_owner(krwlock_t *rwlp)
214 {
215 BUG_ON(rwlp->rw_magic != RW_MAGIC);
216 return rwlp->rw_owner;
217 }
218
219 #ifdef __cplusplus
220 }
221 #endif
222
223 #endif /* _SYS_LINUX_RWLOCK_H */