Line data Source code
1 : /*
2 : * Copyright (c) 2015 Michael Neumann <mneumann@ntecs.de>
3 : * All rights reserved.
4 : *
5 : * Redistribution and use in source and binary forms, with or without
6 : * modification, are permitted provided that the following conditions
7 : * are met:
8 : * 1. Redistributions of source code must retain the above copyright
9 : * notice unmodified, this list of conditions, and the following
10 : * disclaimer.
11 : * 2. Redistributions in binary form must reproduce the above copyright
12 : * notice, this list of conditions and the following disclaimer in the
13 : * documentation and/or other materials provided with the distribution.
14 : *
15 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 : * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 : */
26 :
27 : #ifndef _LINUX_WW_MUTEX_H_
28 : #define _LINUX_WW_MUTEX_H_
29 :
30 : /*
31 : * A basic, unoptimized implementation of wound/wait mutexes for DragonFly
32 : * modelled after the Linux API [1].
33 : *
34 : * [1]: http://lxr.free-electrons.com/source/include/linux/ww_mutex.h
35 : */
36 :
37 : #include <sys/types.h>
38 : #include <sys/systm.h>
39 : #include <sys/mutex.h>
40 :
41 : struct ww_class {
42 : volatile u_long stamp;
43 : const char *name;
44 : };
45 :
46 : struct ww_acquire_ctx {
47 : u_long stamp;
48 : struct ww_class *ww_class;
49 : };
50 :
51 : struct ww_mutex {
52 : struct mutex lock;
53 : volatile int acquired;
54 : volatile struct ww_acquire_ctx *ctx;
55 : volatile struct proc *owner;
56 : };
57 :
58 : #define DEFINE_WW_CLASS(classname) \
59 : struct ww_class classname = { \
60 : .stamp = 0, \
61 : .name = #classname \
62 : }
63 :
64 : static inline void
65 0 : ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *ww_class) {
66 0 : ctx->stamp = __sync_fetch_and_add(&ww_class->stamp, 1);
67 0 : ctx->ww_class = ww_class;
68 0 : }
69 :
70 : static inline void
71 0 : ww_acquire_done(__unused struct ww_acquire_ctx *ctx) {
72 0 : }
73 :
74 : static inline void
75 0 : ww_acquire_fini(__unused struct ww_acquire_ctx *ctx) {
76 0 : }
77 :
78 : static inline void
79 0 : ww_mutex_init(struct ww_mutex *lock, struct ww_class *ww_class) {
80 0 : mtx_init(&lock->lock, IPL_NONE);
81 0 : lock->acquired = 0;
82 0 : lock->ctx = NULL;
83 0 : lock->owner = NULL;
84 0 : }
85 :
86 : static inline bool
87 0 : ww_mutex_is_locked(struct ww_mutex *lock) {
88 : bool res = false;
89 0 : mtx_enter(&lock->lock);
90 0 : if (lock->acquired > 0) res = true;
91 0 : mtx_leave(&lock->lock);
92 0 : return res;
93 : }
94 :
95 : /*
96 : * Return 1 if lock could be acquired, else 0 (contended).
97 : */
98 : static inline int
99 0 : ww_mutex_trylock(struct ww_mutex *lock) {
100 : int res = 0;
101 :
102 0 : mtx_enter(&lock->lock);
103 : /*
104 : * In case no one holds the ww_mutex yet, we acquire it.
105 : */
106 0 : if (lock->acquired == 0) {
107 0 : KASSERT(lock->ctx == NULL);
108 0 : lock->acquired = 1;
109 0 : lock->owner = curproc;
110 : res = 1;
111 0 : }
112 0 : mtx_leave(&lock->lock);
113 0 : return res;
114 : }
115 :
116 : /*
117 : * When `slow` is `true`, it will always block if the ww_mutex is contended.
118 : * It is assumed that the called will not hold any (ww_mutex) resources when
119 : * calling the slow path as this could lead to deadlocks.
120 : *
121 : * When `intr` is `true`, the ssleep will be interruptable.
122 : */
123 : static inline int
124 0 : __ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx, bool slow, bool intr) {
125 : int err;
126 :
127 0 : mtx_enter(&lock->lock);
128 0 : for (;;) {
129 : /*
130 : * In case no one holds the ww_mutex yet, we acquire it.
131 : */
132 0 : if (lock->acquired == 0) {
133 0 : KASSERT(lock->ctx == NULL);
134 0 : lock->acquired = 1;
135 0 : lock->ctx = ctx;
136 0 : lock->owner = curproc;
137 : err = 0;
138 0 : break;
139 : }
140 : /*
141 : * In case we already hold the return -EALREADY.
142 : */
143 0 : else if (lock->owner == curproc) {
144 : err = -EALREADY;
145 0 : break;
146 : }
147 : /*
148 : * This is the contention case where the ww_mutex is
149 : * already held by another context.
150 : */
151 : else {
152 : /*
153 : * Three cases:
154 : *
155 : * - We are in the slow-path (first lock to obtain).
156 : *
157 : * - No context was specified. We assume a single
158 : * resouce, so there is no danger of a deadlock.
159 : *
160 : * - An `older` process (`ctx`) tries to acquire a
161 : * lock already held by a `younger` process.
162 : * We put the `older` process to sleep until
163 : * the `younger` process gives up all it's
164 : * resources.
165 : */
166 0 : if (slow || ctx == NULL ||
167 0 : (lock->ctx && ctx->stamp < lock->ctx->stamp)) {
168 0 : int s = msleep(lock, &lock->lock,
169 0 : intr ? PCATCH : 0,
170 0 : ctx ? ctx->ww_class->name : "ww_mutex_lock", 0);
171 0 : if (intr && (s == EINTR || s == ERESTART)) {
172 : // XXX: Should we handle ERESTART?
173 : err = -EINTR;
174 0 : break;
175 : }
176 0 : }
177 : /*
178 : * If a `younger` process tries to acquire a lock
179 : * already held by an `older` process, we `wound` it,
180 : * i.e. we return -EDEADLK because there is a potential
181 : * risk for a deadlock. The `younger` process then
182 : * should give up all it's resources and try again to
183 : * acquire the lock in question, this time in a
184 : * blocking manner.
185 : */
186 : else {
187 : err = -EDEADLK;
188 0 : break;
189 : }
190 : }
191 :
192 : } /* for */
193 0 : mtx_leave(&lock->lock);
194 0 : return err;
195 : }
196 :
197 : static inline int
198 0 : ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) {
199 0 : return __ww_mutex_lock(lock, ctx, false, false);
200 : }
201 :
202 : static inline void
203 0 : ww_mutex_lock_slow(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) {
204 0 : (void)__ww_mutex_lock(lock, ctx, true, false);
205 0 : }
206 :
207 : static inline int
208 0 : ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) {
209 0 : return __ww_mutex_lock(lock, ctx, false, true);
210 : }
211 :
212 : static inline int __must_check
213 0 : ww_mutex_lock_slow_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) {
214 0 : return __ww_mutex_lock(lock, ctx, true, true);
215 : }
216 :
217 : static inline void
218 0 : ww_mutex_unlock(struct ww_mutex *lock) {
219 0 : mtx_enter(&lock->lock);
220 0 : KASSERT(lock->owner == curproc);
221 0 : KASSERT(lock->acquired == 1);
222 :
223 0 : lock->acquired = 0;
224 0 : lock->ctx = NULL;
225 0 : lock->owner = NULL;
226 0 : mtx_leave(&lock->lock);
227 0 : wakeup(lock);
228 0 : }
229 :
230 : static inline void
231 0 : ww_mutex_destroy(struct ww_mutex *lock) {
232 0 : KASSERT(lock->acquired == 0);
233 0 : KASSERT(lock->ctx == NULL);
234 0 : KASSERT(lock->owner == NULL);
235 0 : }
236 :
237 : #endif /* _LINUX_WW_MUTEX_H_ */
|