Line data Source code
1 : /* $OpenBSD: kern_srp.c,v 1.12 2017/09/08 05:36:53 deraadt Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2014 Jonathan Matthew <jmatthew@openbsd.org>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : #include <sys/param.h>
20 : #include <sys/systm.h>
21 : #include <sys/timeout.h>
22 : #include <sys/srp.h>
23 : #include <sys/atomic.h>
24 :
25 : void srp_v_gc_start(struct srp_gc *, struct srp *, void *);
26 :
27 : void
28 0 : srpl_rc_init(struct srpl_rc *rc, void (*ref)(void *, void *),
29 : void (*unref)(void *, void *), void *cookie)
30 : {
31 0 : rc->srpl_ref = ref;
32 0 : srp_gc_init(&rc->srpl_gc, unref, cookie);
33 0 : }
34 :
35 : void
36 0 : srp_gc_init(struct srp_gc *srp_gc, void (*dtor)(void *, void *), void *cookie)
37 : {
38 0 : srp_gc->srp_gc_dtor = dtor;
39 0 : srp_gc->srp_gc_cookie = cookie;
40 0 : refcnt_init(&srp_gc->srp_gc_refcnt);
41 0 : }
42 :
43 : void
44 0 : srp_init(struct srp *srp)
45 : {
46 0 : srp->ref = NULL;
47 0 : }
48 :
49 : void *
50 0 : srp_swap_locked(struct srp *srp, void *nv)
51 : {
52 : void *ov;
53 :
54 : /*
55 : * this doesn't have to be as careful as the caller has already
56 : * prevented concurrent updates, eg. by holding the kernel lock.
57 : * can't be mixed with non-locked updates though.
58 : */
59 :
60 0 : ov = srp->ref;
61 0 : srp->ref = nv;
62 :
63 0 : return (ov);
64 : }
65 :
66 : void
67 0 : srp_update_locked(struct srp_gc *srp_gc, struct srp *srp, void *v)
68 : {
69 0 : if (v != NULL)
70 0 : refcnt_take(&srp_gc->srp_gc_refcnt);
71 :
72 0 : v = srp_swap_locked(srp, v);
73 :
74 0 : if (v != NULL)
75 0 : srp_v_gc_start(srp_gc, srp, v);
76 0 : }
77 :
78 : void *
79 0 : srp_get_locked(struct srp *srp)
80 : {
81 0 : return (srp->ref);
82 : }
83 :
84 : void
85 0 : srp_gc_finalize(struct srp_gc *srp_gc)
86 : {
87 0 : refcnt_finalize(&srp_gc->srp_gc_refcnt, "srpfini");
88 0 : }
89 :
90 : #ifdef MULTIPROCESSOR
91 : #include <machine/cpu.h>
92 : #include <sys/pool.h>
93 :
94 : struct srp_gc_ctx {
95 : struct srp_gc *srp_gc;
96 : struct timeout tick;
97 : struct srp_hazard hzrd;
98 : };
99 :
100 : int srp_v_referenced(struct srp *, void *);
101 : void srp_v_gc(void *);
102 :
103 : struct pool srp_gc_ctx_pool;
104 :
105 : void
106 0 : srp_startup(void)
107 : {
108 0 : pool_init(&srp_gc_ctx_pool, sizeof(struct srp_gc_ctx), 0,
109 : IPL_SOFTCLOCK, PR_WAITOK, "srpgc", NULL);
110 0 : }
111 :
112 : int
113 0 : srp_v_referenced(struct srp *srp, void *v)
114 : {
115 : struct cpu_info *ci;
116 : CPU_INFO_ITERATOR cii;
117 : u_int i;
118 : struct srp_hazard *hzrd;
119 :
120 0 : CPU_INFO_FOREACH(cii, ci) {
121 0 : for (i = 0; i < nitems(ci->ci_srp_hazards); i++) {
122 0 : hzrd = &ci->ci_srp_hazards[i];
123 :
124 0 : if (hzrd->sh_p != srp)
125 : continue;
126 0 : membar_consumer();
127 0 : if (hzrd->sh_v != v)
128 : continue;
129 :
130 0 : return (1);
131 : }
132 : }
133 :
134 0 : return (0);
135 0 : }
136 :
137 : void
138 0 : srp_v_dtor(struct srp_gc *srp_gc, void *v)
139 : {
140 0 : (*srp_gc->srp_gc_dtor)(srp_gc->srp_gc_cookie, v);
141 :
142 0 : refcnt_rele_wake(&srp_gc->srp_gc_refcnt);
143 0 : }
144 :
145 : void
146 0 : srp_v_gc_start(struct srp_gc *srp_gc, struct srp *srp, void *v)
147 : {
148 : struct srp_gc_ctx *ctx;
149 :
150 0 : if (!srp_v_referenced(srp, v)) {
151 : /* we win */
152 0 : srp_v_dtor(srp_gc, v);
153 0 : return;
154 : }
155 :
156 : /* in use, try later */
157 :
158 0 : ctx = pool_get(&srp_gc_ctx_pool, PR_WAITOK);
159 0 : ctx->srp_gc = srp_gc;
160 0 : ctx->hzrd.sh_p = srp;
161 0 : ctx->hzrd.sh_v = v;
162 :
163 0 : timeout_set(&ctx->tick, srp_v_gc, ctx);
164 0 : timeout_add(&ctx->tick, 1);
165 0 : }
166 :
167 : void
168 0 : srp_v_gc(void *x)
169 : {
170 0 : struct srp_gc_ctx *ctx = x;
171 :
172 0 : if (srp_v_referenced(ctx->hzrd.sh_p, ctx->hzrd.sh_v)) {
173 : /* oh well, try again later */
174 0 : timeout_add(&ctx->tick, 1);
175 0 : return;
176 : }
177 :
178 0 : srp_v_dtor(ctx->srp_gc, ctx->hzrd.sh_v);
179 0 : pool_put(&srp_gc_ctx_pool, ctx);
180 0 : }
181 :
182 : void *
183 0 : srp_swap(struct srp *srp, void *v)
184 : {
185 0 : return (atomic_swap_ptr(&srp->ref, v));
186 : }
187 :
188 : void
189 0 : srp_update(struct srp_gc *srp_gc, struct srp *srp, void *v)
190 : {
191 0 : if (v != NULL)
192 0 : refcnt_take(&srp_gc->srp_gc_refcnt);
193 :
194 0 : v = srp_swap(srp, v);
195 0 : if (v != NULL)
196 0 : srp_v_gc_start(srp_gc, srp, v);
197 0 : }
198 :
199 : static inline void *
200 0 : srp_v(struct srp_hazard *hzrd, struct srp *srp)
201 : {
202 : void *v;
203 :
204 0 : hzrd->sh_p = srp;
205 :
206 : /*
207 : * ensure we update this cpu's hazard pointer to a value that's still
208 : * current after the store finishes, otherwise the gc task may already
209 : * be destroying it
210 : */
211 0 : do {
212 0 : v = srp->ref;
213 0 : hzrd->sh_v = v;
214 0 : membar_consumer();
215 0 : } while (__predict_false(v != srp->ref));
216 :
217 0 : return (v);
218 : }
219 :
220 : void *
221 0 : srp_enter(struct srp_ref *sr, struct srp *srp)
222 : {
223 0 : struct cpu_info *ci = curcpu();
224 : struct srp_hazard *hzrd;
225 : u_int i;
226 :
227 0 : for (i = 0; i < nitems(ci->ci_srp_hazards); i++) {
228 0 : hzrd = &ci->ci_srp_hazards[i];
229 0 : if (hzrd->sh_p == NULL) {
230 0 : sr->hz = hzrd;
231 0 : return (srp_v(hzrd, srp));
232 : }
233 : }
234 :
235 0 : panic("%s: not enough srp hazard records", __func__);
236 :
237 : /* NOTREACHED */
238 : return (NULL);
239 : }
240 :
241 : void *
242 0 : srp_follow(struct srp_ref *sr, struct srp *srp)
243 : {
244 0 : return (srp_v(sr->hz, srp));
245 : }
246 :
247 : void
248 0 : srp_leave(struct srp_ref *sr)
249 : {
250 0 : sr->hz->sh_p = NULL;
251 0 : }
252 :
253 : static inline int
254 0 : srp_referenced(void *v)
255 : {
256 : struct cpu_info *ci;
257 : CPU_INFO_ITERATOR cii;
258 : u_int i;
259 : struct srp_hazard *hzrd;
260 :
261 0 : CPU_INFO_FOREACH(cii, ci) {
262 0 : for (i = 0; i < nitems(ci->ci_srp_hazards); i++) {
263 0 : hzrd = &ci->ci_srp_hazards[i];
264 :
265 0 : if (hzrd->sh_p != NULL && hzrd->sh_v == v)
266 0 : return (1);
267 : }
268 : }
269 :
270 0 : return (0);
271 0 : }
272 :
273 : void
274 0 : srp_finalize(void *v, const char *wmesg)
275 : {
276 0 : while (srp_referenced(v))
277 0 : tsleep(v, PWAIT, wmesg, 1);
278 0 : }
279 :
280 : #else /* MULTIPROCESSOR */
281 :
282 : void
283 : srp_startup(void)
284 : {
285 :
286 : }
287 :
288 : void
289 : srp_v_gc_start(struct srp_gc *srp_gc, struct srp *srp, void *v)
290 : {
291 : (*srp_gc->srp_gc_dtor)(srp_gc->srp_gc_cookie, v);
292 : refcnt_rele_wake(&srp_gc->srp_gc_refcnt);
293 : }
294 :
295 : #endif /* MULTIPROCESSOR */
|