xref: /openbsd-src/sys/kern/kern_srp.c (revision 2777ee89d0e541ec819d05abee114837837abbec)
1 /*	$OpenBSD: kern_srp.c,v 1.7 2015/11/23 10:56:19 mpi 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/types.h>
21 #include <sys/systm.h>
22 #include <sys/timeout.h>
23 #include <sys/srp.h>
24 #include <sys/atomic.h>
25 
26 void	srp_v_gc_start(struct srp_gc *, struct srp *, void *);
27 
28 void
29 srpl_rc_init(struct srpl_rc *rc,  void (*ref)(void *, void *),
30     void (*unref)(void *, void *), void *cookie)
31 {
32 	rc->srpl_ref = ref;
33 	srp_gc_init(&rc->srpl_gc, unref, cookie);
34 }
35 
36 void
37 srp_gc_init(struct srp_gc *srp_gc, void (*dtor)(void *, void *), void *cookie)
38 {
39 	srp_gc->srp_gc_dtor = dtor;
40 	srp_gc->srp_gc_cookie = cookie;
41 	refcnt_init(&srp_gc->srp_gc_refcnt);
42 }
43 
44 void
45 srp_init(struct srp *srp)
46 {
47 	srp->ref = NULL;
48 }
49 
50 void
51 srp_update_locked(struct srp_gc *srp_gc, struct srp *srp, void *nv)
52 {
53 	void *ov;
54 
55 	if (nv != NULL)
56 		refcnt_take(&srp_gc->srp_gc_refcnt);
57 
58 	/*
59 	 * this doesn't have to be as careful as the caller has already
60 	 * prevented concurrent updates, eg. by holding the kernel lock.
61 	 * can't be mixed with non-locked updates though.
62 	 */
63 
64 	ov = srp->ref;
65 	srp->ref = nv;
66 	if (ov != NULL)
67 		srp_v_gc_start(srp_gc, srp, ov);
68 }
69 
70 void *
71 srp_get_locked(struct srp *srp)
72 {
73 	return (srp->ref);
74 }
75 
76 void
77 srp_finalize(struct srp_gc *srp_gc)
78 {
79 	refcnt_finalize(&srp_gc->srp_gc_refcnt, "srpfini");
80 }
81 
82 #ifdef MULTIPROCESSOR
83 #include <machine/cpu.h>
84 #include <sys/pool.h>
85 
86 struct srp_gc_ctx {
87 	struct srp_gc		*srp_gc;
88 	struct timeout		tick;
89 	struct srp_hazard	hzrd;
90 };
91 
92 int	srp_v_referenced(struct srp *, void *);
93 void	srp_v_gc(void *);
94 
95 struct pool srp_gc_ctx_pool;
96 
97 void
98 srp_startup(void)
99 {
100 	pool_init(&srp_gc_ctx_pool, sizeof(struct srp_gc_ctx), 0, 0,
101 	    PR_WAITOK, "srpgc", NULL);
102 
103 	/* items are allocated in a process, but freed from a timeout */
104 	pool_setipl(&srp_gc_ctx_pool, IPL_SOFTCLOCK);
105 }
106 
107 int
108 srp_v_referenced(struct srp *srp, void *v)
109 {
110 	struct cpu_info *ci;
111 	CPU_INFO_ITERATOR cii;
112 	u_int i;
113 	struct srp_hazard *hzrd;
114 
115 	CPU_INFO_FOREACH(cii, ci) {
116 		for (i = 0; i < nitems(ci->ci_srp_hazards); i++) {
117 			hzrd = &ci->ci_srp_hazards[i];
118 
119 			if (hzrd->sh_p != srp)
120 				continue;
121 			membar_consumer();
122 			if (hzrd->sh_v != v)
123 				continue;
124 
125 			return (1);
126 		}
127 	}
128 
129 	return (0);
130 }
131 
132 void
133 srp_v_dtor(struct srp_gc *srp_gc, void *v)
134 {
135 	(*srp_gc->srp_gc_dtor)(srp_gc->srp_gc_cookie, v);
136 
137 	refcnt_rele_wake(&srp_gc->srp_gc_refcnt);
138 }
139 
140 void
141 srp_v_gc_start(struct srp_gc *srp_gc, struct srp *srp, void *v)
142 {
143 	struct srp_gc_ctx *ctx;
144 
145 	if (!srp_v_referenced(srp, v)) {
146 		/* we win */
147 		srp_v_dtor(srp_gc, v);
148 		return;
149 	}
150 
151 	/* in use, try later */
152 
153 	ctx = pool_get(&srp_gc_ctx_pool, PR_WAITOK);
154 	ctx->srp_gc = srp_gc;
155 	ctx->hzrd.sh_p = srp;
156 	ctx->hzrd.sh_v = v;
157 
158 	timeout_set(&ctx->tick, srp_v_gc, ctx);
159 	timeout_add(&ctx->tick, 1);
160 }
161 
162 void
163 srp_v_gc(void *x)
164 {
165 	struct srp_gc_ctx *ctx = x;
166 
167 	if (srp_v_referenced(ctx->hzrd.sh_p, ctx->hzrd.sh_v)) {
168 		/* oh well, try again later */
169 		timeout_add(&ctx->tick, 1);
170 		return;
171 	}
172 
173 	srp_v_dtor(ctx->srp_gc, ctx->hzrd.sh_v);
174 	pool_put(&srp_gc_ctx_pool, ctx);
175 }
176 
177 void
178 srp_update(struct srp_gc *srp_gc, struct srp *srp, void *v)
179 {
180 	if (v != NULL)
181 		refcnt_take(&srp_gc->srp_gc_refcnt);
182 
183 	v = atomic_swap_ptr(&srp->ref, v);
184 	if (v != NULL)
185 		srp_v_gc_start(srp_gc, srp, v);
186 }
187 
188 static inline void *
189 srp_v(struct srp_hazard *hzrd, struct srp *srp)
190 {
191 	void *v;
192 
193 	hzrd->sh_p = srp;
194 
195 	/*
196 	 * ensure we update this cpu's hazard pointer to a value that's still
197 	 * current after the store finishes, otherwise the gc task may already
198 	 * be destroying it
199 	 */
200 	do {
201 		v = srp->ref;
202 		hzrd->sh_v = v;
203 		membar_consumer();
204 	} while (__predict_false(v != srp->ref));
205 
206 	return (v);
207 }
208 
209 void *
210 srp_enter(struct srp *srp)
211 {
212 	struct cpu_info *ci = curcpu();
213 	struct srp_hazard *hzrd;
214 	u_int i;
215 
216 	for (i = 0; i < nitems(ci->ci_srp_hazards); i++) {
217 		hzrd = &ci->ci_srp_hazards[i];
218 		if (hzrd->sh_p == NULL)
219 			return (srp_v(hzrd, srp));
220 	}
221 
222 	panic("%s: not enough srp hazard records", __func__);
223 
224 	/* NOTREACHED */
225 	return (NULL);
226 }
227 
228 void *
229 srp_follow(struct srp *srp, void *v, struct srp *next)
230 {
231 	struct cpu_info *ci = curcpu();
232 	struct srp_hazard *hzrd;
233 
234 	hzrd = ci->ci_srp_hazards + nitems(ci->ci_srp_hazards);
235 	while (hzrd-- != ci->ci_srp_hazards) {
236 		if (hzrd->sh_p == srp && hzrd->sh_v == v)
237 			return (srp_v(hzrd, next));
238 	}
239 
240 	panic("%s: unexpected ref %p via %p", __func__, v, srp);
241 
242 	/* NOTREACHED */
243 	return (NULL);
244 }
245 
246 void
247 srp_leave(struct srp *srp, void *v)
248 {
249 	struct cpu_info *ci = curcpu();
250 	struct srp_hazard *hzrd;
251 
252 	hzrd = ci->ci_srp_hazards + nitems(ci->ci_srp_hazards);
253 	while (hzrd-- != ci->ci_srp_hazards) {
254 		if (hzrd->sh_p == srp && hzrd->sh_v == v) {
255 			hzrd->sh_p = NULL;
256 			return;
257 		}
258 	}
259 
260 	panic("%s: unexpected ref %p via %p", __func__, v, srp);
261 }
262 
263 #else /* MULTIPROCESSOR */
264 
265 void
266 srp_startup(void)
267 {
268 
269 }
270 
271 void
272 srp_v_gc_start(struct srp_gc *srp_gc, struct srp *srp, void *v)
273 {
274 	(*srp_gc->srp_gc_dtor)(srp_gc->srp_gc_cookie, v);
275 	refcnt_rele_wake(&srp_gc->srp_gc_refcnt);
276 }
277 
278 #endif /* MULTIPROCESSOR */
279