1*f55071daScheloha /* $OpenBSD: kern_srp.c,v 1.13 2020/12/06 19:18:30 cheloha Exp $ */
252776b7dSdlg
352776b7dSdlg /*
452776b7dSdlg * Copyright (c) 2014 Jonathan Matthew <jmatthew@openbsd.org>
552776b7dSdlg *
652776b7dSdlg * Permission to use, copy, modify, and distribute this software for any
752776b7dSdlg * purpose with or without fee is hereby granted, provided that the above
852776b7dSdlg * copyright notice and this permission notice appear in all copies.
952776b7dSdlg *
1052776b7dSdlg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1152776b7dSdlg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1252776b7dSdlg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1352776b7dSdlg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1452776b7dSdlg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1552776b7dSdlg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1652776b7dSdlg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1752776b7dSdlg */
1852776b7dSdlg
1952776b7dSdlg #include <sys/param.h>
2052776b7dSdlg #include <sys/systm.h>
210f6f8458Sdlg #include <sys/timeout.h>
2252776b7dSdlg #include <sys/srp.h>
2397f2414eSmpi #include <sys/atomic.h>
2452776b7dSdlg
2552776b7dSdlg void srp_v_gc_start(struct srp_gc *, struct srp *, void *);
2652776b7dSdlg
2752776b7dSdlg void
srpl_rc_init(struct srpl_rc * rc,void (* ref)(void *,void *),void (* unref)(void *,void *),void * cookie)28a81ac0ceSdlg srpl_rc_init(struct srpl_rc *rc, void (*ref)(void *, void *),
29a81ac0ceSdlg void (*unref)(void *, void *), void *cookie)
30a81ac0ceSdlg {
31a81ac0ceSdlg rc->srpl_ref = ref;
32a81ac0ceSdlg srp_gc_init(&rc->srpl_gc, unref, cookie);
33a81ac0ceSdlg }
34a81ac0ceSdlg
35a81ac0ceSdlg void
srp_gc_init(struct srp_gc * srp_gc,void (* dtor)(void *,void *),void * cookie)3652776b7dSdlg srp_gc_init(struct srp_gc *srp_gc, void (*dtor)(void *, void *), void *cookie)
3752776b7dSdlg {
3852776b7dSdlg srp_gc->srp_gc_dtor = dtor;
3952776b7dSdlg srp_gc->srp_gc_cookie = cookie;
400f6f8458Sdlg refcnt_init(&srp_gc->srp_gc_refcnt);
4152776b7dSdlg }
4252776b7dSdlg
4352776b7dSdlg void
srp_init(struct srp * srp)4452776b7dSdlg srp_init(struct srp *srp)
4552776b7dSdlg {
4652776b7dSdlg srp->ref = NULL;
4752776b7dSdlg }
4852776b7dSdlg
491d3aed40Sdlg void *
srp_swap_locked(struct srp * srp,void * nv)501d3aed40Sdlg srp_swap_locked(struct srp *srp, void *nv)
5152776b7dSdlg {
5252776b7dSdlg void *ov;
5352776b7dSdlg
5452776b7dSdlg /*
5552776b7dSdlg * this doesn't have to be as careful as the caller has already
5652776b7dSdlg * prevented concurrent updates, eg. by holding the kernel lock.
5752776b7dSdlg * can't be mixed with non-locked updates though.
5852776b7dSdlg */
5952776b7dSdlg
6052776b7dSdlg ov = srp->ref;
6152776b7dSdlg srp->ref = nv;
621d3aed40Sdlg
631d3aed40Sdlg return (ov);
641d3aed40Sdlg }
651d3aed40Sdlg
661d3aed40Sdlg void
srp_update_locked(struct srp_gc * srp_gc,struct srp * srp,void * v)671d3aed40Sdlg srp_update_locked(struct srp_gc *srp_gc, struct srp *srp, void *v)
681d3aed40Sdlg {
691d3aed40Sdlg if (v != NULL)
701d3aed40Sdlg refcnt_take(&srp_gc->srp_gc_refcnt);
711d3aed40Sdlg
721d3aed40Sdlg v = srp_swap_locked(srp, v);
731d3aed40Sdlg
741d3aed40Sdlg if (v != NULL)
751d3aed40Sdlg srp_v_gc_start(srp_gc, srp, v);
7652776b7dSdlg }
7752776b7dSdlg
7852776b7dSdlg void *
srp_get_locked(struct srp * srp)7952776b7dSdlg srp_get_locked(struct srp *srp)
8052776b7dSdlg {
8152776b7dSdlg return (srp->ref);
8252776b7dSdlg }
8352776b7dSdlg
84e94dd403Sdlg void
srp_gc_finalize(struct srp_gc * srp_gc)8559cbe19bSdlg srp_gc_finalize(struct srp_gc *srp_gc)
86e94dd403Sdlg {
87e94dd403Sdlg refcnt_finalize(&srp_gc->srp_gc_refcnt, "srpfini");
88e94dd403Sdlg }
89e94dd403Sdlg
9052776b7dSdlg #ifdef MULTIPROCESSOR
9152776b7dSdlg #include <machine/cpu.h>
9252776b7dSdlg #include <sys/pool.h>
9352776b7dSdlg
9452776b7dSdlg struct srp_gc_ctx {
9552776b7dSdlg struct srp_gc *srp_gc;
9652776b7dSdlg struct timeout tick;
9752776b7dSdlg struct srp_hazard hzrd;
9852776b7dSdlg };
9952776b7dSdlg
10052776b7dSdlg int srp_v_referenced(struct srp *, void *);
10152776b7dSdlg void srp_v_gc(void *);
10252776b7dSdlg
10352776b7dSdlg struct pool srp_gc_ctx_pool;
10452776b7dSdlg
10552776b7dSdlg void
srp_startup(void)10652776b7dSdlg srp_startup(void)
10752776b7dSdlg {
1081378bae2Sdlg pool_init(&srp_gc_ctx_pool, sizeof(struct srp_gc_ctx), 0,
1091378bae2Sdlg IPL_SOFTCLOCK, PR_WAITOK, "srpgc", NULL);
11052776b7dSdlg }
11152776b7dSdlg
11252776b7dSdlg int
srp_v_referenced(struct srp * srp,void * v)11352776b7dSdlg srp_v_referenced(struct srp *srp, void *v)
11452776b7dSdlg {
11552776b7dSdlg struct cpu_info *ci;
11652776b7dSdlg CPU_INFO_ITERATOR cii;
11752776b7dSdlg u_int i;
11852776b7dSdlg struct srp_hazard *hzrd;
11952776b7dSdlg
12052776b7dSdlg CPU_INFO_FOREACH(cii, ci) {
12152776b7dSdlg for (i = 0; i < nitems(ci->ci_srp_hazards); i++) {
12252776b7dSdlg hzrd = &ci->ci_srp_hazards[i];
12352776b7dSdlg
12452776b7dSdlg if (hzrd->sh_p != srp)
12552776b7dSdlg continue;
12652776b7dSdlg membar_consumer();
12752776b7dSdlg if (hzrd->sh_v != v)
12852776b7dSdlg continue;
12952776b7dSdlg
13052776b7dSdlg return (1);
13152776b7dSdlg }
13252776b7dSdlg }
13352776b7dSdlg
13452776b7dSdlg return (0);
13552776b7dSdlg }
13652776b7dSdlg
13752776b7dSdlg void
srp_v_dtor(struct srp_gc * srp_gc,void * v)13852776b7dSdlg srp_v_dtor(struct srp_gc *srp_gc, void *v)
13952776b7dSdlg {
14052776b7dSdlg (*srp_gc->srp_gc_dtor)(srp_gc->srp_gc_cookie, v);
14152776b7dSdlg
1420f6f8458Sdlg refcnt_rele_wake(&srp_gc->srp_gc_refcnt);
14352776b7dSdlg }
14452776b7dSdlg
14552776b7dSdlg void
srp_v_gc_start(struct srp_gc * srp_gc,struct srp * srp,void * v)14652776b7dSdlg srp_v_gc_start(struct srp_gc *srp_gc, struct srp *srp, void *v)
14752776b7dSdlg {
14852776b7dSdlg struct srp_gc_ctx *ctx;
14952776b7dSdlg
15052776b7dSdlg if (!srp_v_referenced(srp, v)) {
15152776b7dSdlg /* we win */
15252776b7dSdlg srp_v_dtor(srp_gc, v);
15352776b7dSdlg return;
15452776b7dSdlg }
15552776b7dSdlg
15652776b7dSdlg /* in use, try later */
15752776b7dSdlg
15852776b7dSdlg ctx = pool_get(&srp_gc_ctx_pool, PR_WAITOK);
15952776b7dSdlg ctx->srp_gc = srp_gc;
16052776b7dSdlg ctx->hzrd.sh_p = srp;
16152776b7dSdlg ctx->hzrd.sh_v = v;
16252776b7dSdlg
16352776b7dSdlg timeout_set(&ctx->tick, srp_v_gc, ctx);
16452776b7dSdlg timeout_add(&ctx->tick, 1);
16552776b7dSdlg }
16652776b7dSdlg
16752776b7dSdlg void
srp_v_gc(void * x)16852776b7dSdlg srp_v_gc(void *x)
16952776b7dSdlg {
17052776b7dSdlg struct srp_gc_ctx *ctx = x;
17152776b7dSdlg
17252776b7dSdlg if (srp_v_referenced(ctx->hzrd.sh_p, ctx->hzrd.sh_v)) {
17352776b7dSdlg /* oh well, try again later */
17452776b7dSdlg timeout_add(&ctx->tick, 1);
17552776b7dSdlg return;
17652776b7dSdlg }
17752776b7dSdlg
17852776b7dSdlg srp_v_dtor(ctx->srp_gc, ctx->hzrd.sh_v);
17952776b7dSdlg pool_put(&srp_gc_ctx_pool, ctx);
18052776b7dSdlg }
18152776b7dSdlg
1821d3aed40Sdlg void *
srp_swap(struct srp * srp,void * v)1831d3aed40Sdlg srp_swap(struct srp *srp, void *v)
1841d3aed40Sdlg {
1851d3aed40Sdlg return (atomic_swap_ptr(&srp->ref, v));
1861d3aed40Sdlg }
1871d3aed40Sdlg
18852776b7dSdlg void
srp_update(struct srp_gc * srp_gc,struct srp * srp,void * v)18952776b7dSdlg srp_update(struct srp_gc *srp_gc, struct srp *srp, void *v)
19052776b7dSdlg {
19152776b7dSdlg if (v != NULL)
1920f6f8458Sdlg refcnt_take(&srp_gc->srp_gc_refcnt);
19352776b7dSdlg
1941d3aed40Sdlg v = srp_swap(srp, v);
19552776b7dSdlg if (v != NULL)
19652776b7dSdlg srp_v_gc_start(srp_gc, srp, v);
19752776b7dSdlg }
19852776b7dSdlg
199123afffaSdlg static inline void *
srp_v(struct srp_hazard * hzrd,struct srp * srp)200123afffaSdlg srp_v(struct srp_hazard *hzrd, struct srp *srp)
20152776b7dSdlg {
20252776b7dSdlg void *v;
20352776b7dSdlg
20452776b7dSdlg hzrd->sh_p = srp;
20552776b7dSdlg
20652776b7dSdlg /*
20752776b7dSdlg * ensure we update this cpu's hazard pointer to a value that's still
20852776b7dSdlg * current after the store finishes, otherwise the gc task may already
20952776b7dSdlg * be destroying it
21052776b7dSdlg */
21152776b7dSdlg do {
21252776b7dSdlg v = srp->ref;
21352776b7dSdlg hzrd->sh_v = v;
21452776b7dSdlg membar_consumer();
21552776b7dSdlg } while (__predict_false(v != srp->ref));
21652776b7dSdlg
21752776b7dSdlg return (v);
21852776b7dSdlg }
21952776b7dSdlg
220123afffaSdlg void *
srp_enter(struct srp_ref * sr,struct srp * srp)221ca268887Sdlg srp_enter(struct srp_ref *sr, struct srp *srp)
22252776b7dSdlg {
22352776b7dSdlg struct cpu_info *ci = curcpu();
22452776b7dSdlg struct srp_hazard *hzrd;
22552776b7dSdlg u_int i;
22652776b7dSdlg
22752776b7dSdlg for (i = 0; i < nitems(ci->ci_srp_hazards); i++) {
22852776b7dSdlg hzrd = &ci->ci_srp_hazards[i];
229ca268887Sdlg if (hzrd->sh_p == NULL) {
230ca268887Sdlg sr->hz = hzrd;
231123afffaSdlg return (srp_v(hzrd, srp));
232123afffaSdlg }
233ca268887Sdlg }
234123afffaSdlg
235123afffaSdlg panic("%s: not enough srp hazard records", __func__);
236123afffaSdlg
237123afffaSdlg /* NOTREACHED */
238123afffaSdlg return (NULL);
239123afffaSdlg }
240123afffaSdlg
241123afffaSdlg void *
srp_follow(struct srp_ref * sr,struct srp * srp)242ca268887Sdlg srp_follow(struct srp_ref *sr, struct srp *srp)
243123afffaSdlg {
244ca268887Sdlg return (srp_v(sr->hz, srp));
245123afffaSdlg }
246123afffaSdlg
247123afffaSdlg void
srp_leave(struct srp_ref * sr)248ca268887Sdlg srp_leave(struct srp_ref *sr)
249123afffaSdlg {
250ca268887Sdlg sr->hz->sh_p = NULL;
25152776b7dSdlg }
25252776b7dSdlg
2531d3aed40Sdlg static inline int
srp_referenced(void * v)2541d3aed40Sdlg srp_referenced(void *v)
2551d3aed40Sdlg {
2561d3aed40Sdlg struct cpu_info *ci;
2571d3aed40Sdlg CPU_INFO_ITERATOR cii;
2581d3aed40Sdlg u_int i;
2591d3aed40Sdlg struct srp_hazard *hzrd;
2601d3aed40Sdlg
2611d3aed40Sdlg CPU_INFO_FOREACH(cii, ci) {
2621d3aed40Sdlg for (i = 0; i < nitems(ci->ci_srp_hazards); i++) {
2631d3aed40Sdlg hzrd = &ci->ci_srp_hazards[i];
2641d3aed40Sdlg
2651d3aed40Sdlg if (hzrd->sh_p != NULL && hzrd->sh_v == v)
2661d3aed40Sdlg return (1);
2671d3aed40Sdlg }
2681d3aed40Sdlg }
2691d3aed40Sdlg
2701d3aed40Sdlg return (0);
2711d3aed40Sdlg }
2721d3aed40Sdlg
2731d3aed40Sdlg void
srp_finalize(void * v,const char * wmesg)2741d3aed40Sdlg srp_finalize(void *v, const char *wmesg)
2751d3aed40Sdlg {
2761d3aed40Sdlg while (srp_referenced(v))
277*f55071daScheloha tsleep_nsec(v, PWAIT, wmesg, MSEC_TO_NSEC(1));
2781d3aed40Sdlg }
2791d3aed40Sdlg
28052776b7dSdlg #else /* MULTIPROCESSOR */
28152776b7dSdlg
28252776b7dSdlg void
srp_startup(void)28352776b7dSdlg srp_startup(void)
28452776b7dSdlg {
28552776b7dSdlg
28652776b7dSdlg }
28752776b7dSdlg
28852776b7dSdlg void
srp_v_gc_start(struct srp_gc * srp_gc,struct srp * srp,void * v)28952776b7dSdlg srp_v_gc_start(struct srp_gc *srp_gc, struct srp *srp, void *v)
29052776b7dSdlg {
29152776b7dSdlg (*srp_gc->srp_gc_dtor)(srp_gc->srp_gc_cookie, v);
292e94dd403Sdlg refcnt_rele_wake(&srp_gc->srp_gc_refcnt);
29352776b7dSdlg }
29452776b7dSdlg
29552776b7dSdlg #endif /* MULTIPROCESSOR */
296