geom_event.c revision 114450
1/*- 2 * Copyright (c) 2002 Poul-Henning Kamp 3 * Copyright (c) 2002 Networks Associates Technology, Inc. 4 * All rights reserved. 5 * 6 * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7 * and NAI Labs, the Security Research Division of Network Associates, Inc. 8 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9 * DARPA CHATS research program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. The names of the authors may not be used to endorse or promote 20 * products derived from this software without specific prior written 21 * permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * $FreeBSD: head/sys/geom/geom_event.c 114450 2003-05-01 19:43:52Z phk $ 36 */ 37 38/* 39 * XXX: How do we in general know that objects referenced in events 40 * have not been destroyed before we get around to handle the event ? 41 */ 42 43#include <sys/param.h> 44#include <sys/malloc.h> 45#include <sys/systm.h> 46#include <sys/kernel.h> 47#include <sys/lock.h> 48#include <sys/mutex.h> 49#include <machine/stdarg.h> 50#include <sys/errno.h> 51#include <sys/time.h> 52#include <geom/geom.h> 53#include <geom/geom_int.h> 54 55TAILQ_HEAD(event_tailq_head, g_event); 56 57static struct event_tailq_head g_events = TAILQ_HEAD_INITIALIZER(g_events); 58static u_int g_pending_events; 59static TAILQ_HEAD(,g_provider) g_doorstep = TAILQ_HEAD_INITIALIZER(g_doorstep); 60static struct mtx g_eventlock; 61static struct sx g_eventstall; 62 63#define G_N_EVENTREFS 20 64 65struct g_event { 66 TAILQ_ENTRY(g_event) events; 67 g_event_t *func; 68 void *arg; 69 int flag; 70 void *ref[G_N_EVENTREFS]; 71}; 72 73#define EV_DONE 0x80000 74#define EV_WAKEUP 0x40000 75#define EV_CANCELED 0x20000 76 77void 78g_waitidle(void) 79{ 80 81 while (g_pending_events) 82 tsleep(&g_pending_events, PPAUSE, "g_waitidle", hz/5); 83} 84 85void 86g_stall_events(void) 87{ 88 89 sx_xlock(&g_eventstall); 90} 91 92void 93g_release_events(void) 94{ 95 96 sx_xunlock(&g_eventstall); 97} 98 99void 100g_orphan_provider(struct g_provider *pp, int error) 101{ 102 103 g_trace(G_T_TOPOLOGY, "g_orphan_provider(%p(%s), %d)", 104 pp, pp->name, error); 105 KASSERT(error != 0, 106 ("g_orphan_provider(%p(%s), 0) error must be non-zero\n", 107 pp, pp->name)); 108 pp->error = error; 109 mtx_lock(&g_eventlock); 110 TAILQ_INSERT_TAIL(&g_doorstep, pp, orphan); 111 mtx_unlock(&g_eventlock); 112 wakeup(&g_wait_event); 113} 114 115/* 116 * This function is called once on each provider which the event handler 117 * finds on its g_doorstep. 118 */ 119 120static void 121g_orphan_register(struct g_provider *pp) 122{ 123 struct g_consumer *cp, *cp2; 124 125 g_trace(G_T_TOPOLOGY, "g_orphan_register(%s)", pp->name); 126 g_topology_assert(); 127 128 /* 129 * Tell all consumers the bad news. 130 * Don't be surprised if they self-destruct. 131 */ 132 cp = LIST_FIRST(&pp->consumers); 133 while (cp != NULL) { 134 cp2 = LIST_NEXT(cp, consumers); 135 KASSERT(cp->geom->orphan != NULL, 136 ("geom %s has no orphan, class %s", 137 cp->geom->name, cp->geom->class->name)); 138 cp->geom->orphan(cp); 139 cp = cp2; 140 } 141#ifdef notyet 142 cp = LIST_FIRST(&pp->consumers); 143 if (cp != NULL) 144 return; 145 if (pp->geom->flags & G_GEOM_WITHER) 146 g_destroy_provider(pp); 147#endif 148} 149 150static void 151g_destroy_event(struct g_event *ep) 152{ 153 154 g_free(ep); 155} 156 157static int 158one_event(void) 159{ 160 struct g_event *ep; 161 struct g_provider *pp; 162 163 sx_xlock(&g_eventstall); 164 g_topology_lock(); 165 for (;;) { 166 mtx_lock(&g_eventlock); 167 pp = TAILQ_FIRST(&g_doorstep); 168 if (pp != NULL) 169 TAILQ_REMOVE(&g_doorstep, pp, orphan); 170 mtx_unlock(&g_eventlock); 171 if (pp == NULL) 172 break; 173 g_orphan_register(pp); 174 } 175 mtx_lock(&g_eventlock); 176 ep = TAILQ_FIRST(&g_events); 177 if (ep == NULL) { 178 mtx_unlock(&g_eventlock); 179 g_topology_unlock(); 180 sx_xunlock(&g_eventstall); 181 return (0); 182 } 183 TAILQ_REMOVE(&g_events, ep, events); 184 mtx_unlock(&g_eventlock); 185 g_topology_assert(); 186 ep->func(ep->arg, 0); 187 g_topology_assert(); 188 if (ep->flag & EV_WAKEUP) { 189 ep->flag |= EV_DONE; 190 wakeup(ep); 191 } else { 192 g_destroy_event(ep); 193 } 194 g_pending_events--; 195 if (g_pending_events == 0) 196 wakeup(&g_pending_events); 197 g_topology_unlock(); 198 sx_xunlock(&g_eventstall); 199 return (1); 200} 201 202void 203g_run_events() 204{ 205 206 while (one_event()) 207 ; 208} 209 210void 211g_cancel_event(void *ref) 212{ 213 struct g_event *ep, *epn; 214 struct g_provider *pp; 215 u_int n; 216 217 mtx_lock(&g_eventlock); 218 TAILQ_FOREACH(pp, &g_doorstep, orphan) { 219 if (pp != ref) 220 continue; 221 TAILQ_REMOVE(&g_doorstep, pp, orphan); 222 break; 223 } 224 for (ep = TAILQ_FIRST(&g_events); ep != NULL; ep = epn) { 225 epn = TAILQ_NEXT(ep, events); 226 for (n = 0; n < G_N_EVENTREFS; n++) { 227 if (ep->ref[n] == NULL) 228 break; 229 if (ep->ref[n] == ref) { 230 TAILQ_REMOVE(&g_events, ep, events); 231 ep->func(ep->arg, EV_CANCEL); 232 if (ep->flag & EV_WAKEUP) { 233 ep->flag |= EV_DONE; 234 ep->flag |= EV_CANCELED; 235 wakeup(ep); 236 } else { 237 g_destroy_event(ep); 238 } 239 break; 240 } 241 } 242 } 243 mtx_unlock(&g_eventlock); 244} 245 246static int 247g_post_event_x(g_event_t *func, void *arg, int flag, struct g_event **epp, va_list ap) 248{ 249 struct g_event *ep; 250 void *p; 251 u_int n; 252 253 g_trace(G_T_TOPOLOGY, "g_post_event_x(%p, %p, %d", func, arg, flag); 254 ep = g_malloc(sizeof *ep, flag | M_ZERO); 255 if (ep == NULL) 256 return (ENOMEM); 257 ep->flag = flag; 258 for (n = 0; n < G_N_EVENTREFS; n++) { 259 p = va_arg(ap, void *); 260 if (p == NULL) 261 break; 262 g_trace(G_T_TOPOLOGY, " ref %p", p); 263 ep->ref[n++] = p; 264 } 265 va_end(ap); 266 KASSERT(p == NULL, ("Too many references to event")); 267 ep->func = func; 268 ep->arg = arg; 269 mtx_lock(&g_eventlock); 270 g_pending_events++; 271 TAILQ_INSERT_TAIL(&g_events, ep, events); 272 mtx_unlock(&g_eventlock); 273 wakeup(&g_wait_event); 274 if (epp != NULL) 275 *epp = ep; 276 return (0); 277} 278 279int 280g_post_event(g_event_t *func, void *arg, int flag, ...) 281{ 282 va_list ap; 283 284 va_start(ap, flag); 285 KASSERT(flag == M_WAITOK || flag == M_NOWAIT, 286 ("Wrong flag to g_post_event")); 287 return (g_post_event_x(func, arg, flag, NULL, ap)); 288} 289 290 291/* 292 * XXX: It might actually be useful to call this function with topology held. 293 * XXX: This would ensure that the event gets created before anything else 294 * XXX: changes. At present all users have a handle on things in some other 295 * XXX: way, so this remains an XXX for now. 296 */ 297 298int 299g_waitfor_event(g_event_t *func, void *arg, int flag, ...) 300{ 301 va_list ap; 302 struct g_event *ep; 303 int error; 304 305 /* g_topology_assert_not(); */ 306 va_start(ap, flag); 307 KASSERT(flag == M_WAITOK || flag == M_NOWAIT, 308 ("Wrong flag to g_post_event")); 309 error = g_post_event_x(func, arg, flag | EV_WAKEUP, &ep, ap); 310 if (error) 311 return (error); 312 do 313 tsleep(ep, PRIBIO, "g_waitfor_event", hz); 314 while (!(ep->flag & EV_DONE)); 315 if (ep->flag & EV_CANCELED) 316 error = EAGAIN; 317 g_destroy_event(ep); 318 return (error); 319} 320 321void 322g_event_init() 323{ 324 325 mtx_init(&g_eventlock, "GEOM orphanage", NULL, MTX_DEF); 326 sx_init(&g_eventstall, "GEOM event stalling"); 327} 328