Index: sys/srp.h =================================================================== RCS file: sys/srp.h diff -N sys/srp.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/srp.h 6 Feb 2015 03:00:52 -0000 @@ -0,0 +1,44 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2014 Jonathan Matthew + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SYS_SRP_H_ +#define _SYS_SRP_H_ + +struct srp_value { + void *v; +}; + +struct srp { + void (*srp_v_dtor)(void *, void *); + void *srp_cookie; + u_int srp_refcount; +}; + +#define SRP_INITIALIZER(_d, _c) { (_d), (_c), 1 } + +void srp_init(struct srp *, + void (*)(void *, void *), void *); +void srp_update(struct srp *, struct srp_value *, void *); +void srp_update_locked(struct srp *, struct srp_value *, void *); +const void * srp_get_locked(struct srp *, struct srp_value *); +void srp_finalize(struct srp *); + +const void * srp_enter(struct srp *, struct srp_value *); +void srp_leave(struct srp *, struct srp_value *, const void *); + +#endif /* _SYS_SRP_H_ */ Index: kern/kern_srp.c =================================================================== RCS file: kern/kern_srp.c diff -N kern/kern_srp.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ kern/kern_srp.c 6 Feb 2015 03:00:52 -0000 @@ -0,0 +1,218 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2014 Jonathan Matthew + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct srp_gc_ctx { + struct srp *srp; + struct timeout tick; + void *v; +}; + +int srp_v_referenced(struct srp *, void *); +void srp_v_gc_start(struct srp *, void *); +void srp_v_gc(void *); + +struct pool srp_gc_ctx_pool; + +void +srp_init_subsys(void) +{ + pool_init(&srp_gc_ctx_pool, sizeof(struct srp_gc_ctx), 0, 0, + PR_WAITOK, "srpgc", NULL); +} + +int +srp_v_referenced(struct srp *srp, void *v) +{ + struct cpu_info *ci; + CPU_INFO_ITERATOR cii; + u_int i; + + CPU_INFO_FOREACH(cii, ci) { + for (i = 0; i < nitems(ci->ci_srp_hazards); i++) { + if (ci->ci_srp_hazards[i] == v) + return (1); + } + } + + return (0); +} + +void +srp_v_dtor(struct srp *srp, void *v) +{ + (*srp->srp_v_dtor)(srp->srp_cookie, v); + + if (atomic_dec_int_nv(&srp->srp_refcount) == 0) + wakeup_one(&srp->srp_refcount); +} + +void +srp_v_gc_start(struct srp *srp, void *v) +{ + if (srp_v_referenced(srp, v)) { + struct srp_gc_ctx *ctx; + + ctx = pool_get(&srp_gc_ctx_pool, PR_WAITOK); + ctx->srp = srp; + ctx->v = v; + + timeout_set(&ctx->tick, srp_v_gc, ctx); + timeout_add(&ctx->tick, 1); + + /* in use, try later */ + return; + } + + srp_v_dtor(srp, v); +} + +void +srp_v_gc(void *x) +{ + struct srp_gc_ctx *ctx = x; + struct srp *srp = ctx->srp; + void *v = ctx->v; + + if (srp_v_referenced(srp, v)) { + /* oh well, try again later */ + timeout_add(&ctx->tick, 1); + return; + } + + pool_put(&srp_gc_ctx_pool, ctx); + + srp_v_dtor(srp, v); +} + +void +srp_init(struct srp *srp, void (*v_dtor)(void *, void *), void *cookie) +{ + srp->srp_v_dtor = v_dtor; + srp->srp_cookie = cookie; + srp->srp_refcount = 1; +} + +void +srp_finalize(struct srp *srp) +{ + struct sleep_state sls; + u_int r; + + r = atomic_dec_int_nv(&srp->srp_refcount); + while (r > 0) { + sleep_setup(&sls, &srp->srp_refcount, PWAIT, "srpfini"); + r = srp->srp_refcount; + sleep_finish(&sls, r); + } +} + +const void * +srp_enter(struct srp *srp, struct srp_value *srp_v) +{ + struct cpu_info *ci = curcpu(); + void *v; + u_int i; + + for (i = 0; i < nitems(ci->ci_srp_hazards); i++) { + if (ci->ci_srp_hazards[i] == NULL) + break; + } + if (__predict_false(i == nitems(ci->ci_srp_hazards))) + panic("%s: not enough srp hazard records", __func__); + + /* + * ensure we update this cpu's hazard pointer to a value that's still + * current after the store finishes, otherwise the gc task may already + * be destroying it + */ + do { + v = srp_v->v; + ci->ci_srp_hazards[i] = v; + membar_consumer(); + } while (__predict_false(v != srp_v->v)); + + return (v); +} + +void +srp_leave(struct srp *hp, struct srp_value *srp_v, const void *v) +{ + struct cpu_info *ci = curcpu(); + u_int i; + + if (v == NULL) + return; + + for (i = 0; i < nitems(ci->ci_srp_hazards); i++) { + if (v == ci->ci_srp_hazards[i]) { + ci->ci_srp_hazards[i] = NULL; + return; + } + } + + panic("%s: unexpected value %p", __func__, v); +} + +void +srp_update(struct srp *srp, struct srp_value *srp_v, void *v) +{ + if (v != NULL) + atomic_inc_int(&srp->srp_refcount); + + v = atomic_swap_ptr(&srp_v->v, v); + membar_producer(); + if (v != NULL) + srp_v_gc_start(srp, v); +} + +void +srp_update_locked(struct srp *srp, struct srp_value *srp_v, void *nv) +{ + void *ov; + + if (nv != NULL) + atomic_inc_int(&srp->srp_refcount); + + /* + * this doesn't have to be as careful as the caller has already + * prevented concurrent updates, eg. by holding the kernel lock. + * can't be mixed with non-locked updates though. + */ + ov = srp_v->v; + srp_v->v = nv; + membar_producer(); + + if (ov != NULL) + srp_v_gc_start(srp, ov); +} + +const void * +srp_get_locked(struct srp *srp, struct srp_value *srp_v) +{ + return (srp_v->v); +}