diff --git a/sys/geom/geom.h b/sys/geom/geom.h index b05fb1d95403..80d294924d16 100644 --- a/sys/geom/geom.h +++ b/sys/geom/geom.h @@ -211,6 +211,7 @@ void g_trace(int level, const char *, ...); typedef void g_event_t(void *, int flag); #define EV_CANCEL 1 int g_post_event(g_event_t *func, void *arg, int flag, ...); +int g_waitfor_event(g_event_t *func, void *arg, int flag, ...); void g_cancel_event(void *ref); void g_orphan_provider(struct g_provider *pp, int error); void g_waitidle(void); diff --git a/sys/geom/geom_disk.c b/sys/geom/geom_disk.c index 91887b869f2f..ec2e9b81785b 100644 --- a/sys/geom/geom_disk.c +++ b/sys/geom/geom_disk.c @@ -375,7 +375,6 @@ g_kern_disks(void *p, int flag __unused) sp = " "; } sbuf_finish(sb); - wakeup(sb); } static int @@ -386,10 +385,7 @@ sysctl_disks(SYSCTL_HANDLER_ARGS) sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); sbuf_clear(sb); - g_post_event(g_kern_disks, sb, M_WAITOK, NULL); - while (!sbuf_done(sb)) { - tsleep(sb, PZERO, "kern.disks", hz); - } + g_waitfor_event(g_kern_disks, sb, M_WAITOK, NULL); error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); sbuf_delete(sb); return error; diff --git a/sys/geom/geom_dump.c b/sys/geom/geom_dump.c index 3f064b3334c3..c56a25df5013 100644 --- a/sys/geom/geom_dump.c +++ b/sys/geom/geom_dump.c @@ -106,7 +106,6 @@ g_confdot(void *p, int flag ) g_confdot_class(sb, mp); sbuf_printf(sb, "};\n"); sbuf_finish(sb); - wakeup(p); } static void @@ -150,7 +149,6 @@ g_conftxt(void *p, int flag) if (mp != NULL) g_conftxt_class(sb, mp); sbuf_finish(sb); - wakeup(p); } @@ -260,7 +258,6 @@ g_confxml(void *p, int flag) KASSERT(flag != EV_CANCEL, ("g_confxml was cancelled")); g_topology_assert(); g_conf_specific(p, NULL, NULL, NULL, NULL); - wakeup(p); } void diff --git a/sys/geom/geom_event.c b/sys/geom/geom_event.c index babff65431f4..0add6cf1411f 100644 --- a/sys/geom/geom_event.c +++ b/sys/geom/geom_event.c @@ -64,11 +64,15 @@ static struct sx g_eventstall; struct g_event { TAILQ_ENTRY(g_event) events; - void *arg; g_event_t *func; + void *arg; + int flag; void *ref[G_N_EVENTREFS]; }; +#define EV_DONE 0x80000 +#define EV_WAKEUP 0x40000 + void g_waitidle(void) { @@ -180,7 +184,12 @@ one_event(void) g_topology_assert(); ep->func(ep->arg, 0); g_topology_assert(); - g_destroy_event(ep); + if (ep->flag & EV_WAKEUP) { + ep->flag |= EV_DONE; + wakeup(ep); + } else { + g_destroy_event(ep); + } g_pending_events--; if (g_pending_events == 0) wakeup(&g_pending_events); @@ -212,7 +221,12 @@ g_cancel_event(void *ref) if (ep->ref[n] == ref) { TAILQ_REMOVE(&g_events, ep, events); ep->func(ep->arg, EV_CANCEL); - g_free(ep); + if (ep->flag & EV_WAKEUP) { + ep->flag |= EV_DONE; + wakeup(ep); + } else { + g_destroy_event(ep); + } break; } } @@ -220,21 +234,18 @@ g_cancel_event(void *ref) mtx_unlock(&g_eventlock); } -int -g_post_event(g_event_t *func, void *arg, int flag, ...) +static int +g_post_event_x(g_event_t *func, void *arg, int flag, struct g_event **epp, va_list ap) { struct g_event *ep; - va_list ap; void *p; u_int n; - g_trace(G_T_TOPOLOGY, "g_post_event(%p, %p, %d", func, arg, flag); - KASSERT(flag == M_NOWAIT || flag == M_WAITOK, - ("Wrong flag to g_post_event")); + g_trace(G_T_TOPOLOGY, "g_post_event_x(%p, %p, %d", func, arg, flag); ep = g_malloc(sizeof *ep, flag | M_ZERO); if (ep == NULL) return (ENOMEM); - va_start(ap, flag); + ep->flag = flag; for (n = 0; n < G_N_EVENTREFS; n++) { p = va_arg(ap, void *); if (p == NULL) @@ -251,6 +262,48 @@ g_post_event(g_event_t *func, void *arg, int flag, ...) TAILQ_INSERT_TAIL(&g_events, ep, events); mtx_unlock(&g_eventlock); wakeup(&g_wait_event); + if (epp != NULL) + *epp = ep; + return (0); +} + +int +g_post_event(g_event_t *func, void *arg, int flag, ...) +{ + va_list ap; + + va_start(ap, flag); + KASSERT(flag == M_WAITOK || flag == M_NOWAIT, + ("Wrong flag to g_post_event")); + return (g_post_event_x(func, arg, flag, NULL, ap)); +} + + +/* + * XXX: It might actually be useful to call this function with topology held. + * XXX: This would ensure that the event gets created before anything else + * XXX: changes. At present all users have a handle on things in some other + * XXX: way, so this remains an XXX for now. + */ + +int +g_waitfor_event(g_event_t *func, void *arg, int flag, ...) +{ + va_list ap; + struct g_event *ep; + int error; + + /* g_topology_assert_not(); */ + va_start(ap, flag); + KASSERT(flag == M_WAITOK || flag == M_NOWAIT, + ("Wrong flag to g_post_event")); + error = g_post_event_x(func, arg, flag | EV_WAKEUP, &ep, ap); + if (error) + return (error); + do + tsleep(ep, PRIBIO, "g_waitfor_event", hz); + while (!(ep->flag & EV_DONE)); + g_destroy_event(ep); return (0); } diff --git a/sys/geom/geom_kern.c b/sys/geom/geom_kern.c index 38531b75cecd..aa44b9fa4c69 100644 --- a/sys/geom/geom_kern.c +++ b/sys/geom/geom_kern.c @@ -172,10 +172,7 @@ sysctl_kern_geom_conftxt(SYSCTL_HANDLER_ARGS) sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); sbuf_clear(sb); - g_post_event(g_conftxt, sb, M_WAITOK, NULL); - do { - tsleep(sb, PZERO, "g_conftxt", hz); - } while(!sbuf_done(sb)); + g_waitfor_event(g_conftxt, sb, M_WAITOK, NULL); error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); sbuf_delete(sb); return error; @@ -189,10 +186,7 @@ sysctl_kern_geom_confdot(SYSCTL_HANDLER_ARGS) sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); sbuf_clear(sb); - g_post_event(g_confdot, sb, M_WAITOK, NULL); - do { - tsleep(sb, PZERO, "g_confdot", hz); - } while(!sbuf_done(sb)); + g_waitfor_event(g_confdot, sb, M_WAITOK, NULL); error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); sbuf_delete(sb); return error; @@ -206,10 +200,7 @@ sysctl_kern_geom_confxml(SYSCTL_HANDLER_ARGS) sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); sbuf_clear(sb); - g_post_event(g_confxml, sb, M_WAITOK, NULL); - do { - tsleep(sb, PZERO, "g_confxml", hz); - } while(!sbuf_done(sb)); + g_waitfor_event(g_confxml, sb, M_WAITOK, NULL); error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); sbuf_delete(sb); return error; diff --git a/sys/geom/geom_sunlabel.c b/sys/geom/geom_sunlabel.c index edd3e6f84af9..a8891af8170a 100644 --- a/sys/geom/geom_sunlabel.c +++ b/sys/geom/geom_sunlabel.c @@ -164,7 +164,6 @@ g_sunlabel_callconfig(void *arg, int flag) if (!hp->error) hp->error = g_write_data(LIST_FIRST(&hp->gp->consumer), 0, hp->label, SUN_SIZE); - wakeup(hp); } /* @@ -194,11 +193,8 @@ g_sunlabel_config(struct gctl_req *req, struct g_geom *gp, const char *verb) error = g_access_rel(cp, 1, 1, 1); if (error) return (error); - g_post_event(g_sunlabel_callconfig, &h0h0, M_WAITOK, gp, NULL); g_topology_unlock(); - do - tsleep(&h0h0, PRIBIO, "g_sunlabel_config", hz); - while (h0h0.error == -1); + g_waitfor_event(g_sunlabel_callconfig, &h0h0, M_WAITOK, gp, NULL); g_topology_lock(); error = h0h0.error; g_access_rel(cp, -1, -1, -1);