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