geom_event.c revision 126798
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
36/*
37 * XXX: How do we in general know that objects referenced in events
38 * have not been destroyed before we get around to handle the event ?
39 */
40
41#include <sys/cdefs.h>
42__FBSDID("$FreeBSD: head/sys/geom/geom_event.c 126798 2004-03-10 08:49:08Z phk $");
43
44#include <sys/param.h>
45#include <sys/malloc.h>
46#include <sys/systm.h>
47#include <sys/kernel.h>
48#include <sys/lock.h>
49#include <sys/mutex.h>
50#include <machine/stdarg.h>
51#include <sys/errno.h>
52#include <sys/time.h>
53#include <geom/geom.h>
54#include <geom/geom_int.h>
55
56TAILQ_HEAD(event_tailq_head, g_event);
57
58static struct event_tailq_head g_events = TAILQ_HEAD_INITIALIZER(g_events);
59static u_int g_pending_events;
60static TAILQ_HEAD(,g_provider) g_doorstep = TAILQ_HEAD_INITIALIZER(g_doorstep);
61static struct mtx g_eventlock;
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_orphan_provider(struct g_provider *pp, int error)
87{
88
89	/* G_VALID_PROVIDER(pp)  We likely lack topology lock */
90	g_trace(G_T_TOPOLOGY, "g_orphan_provider(%p(%s), %d)",
91	    pp, pp->name, error);
92	KASSERT(error != 0,
93	    ("g_orphan_provider(%p(%s), 0) error must be non-zero\n",
94	     pp, pp->name));
95
96	pp->error = error;
97	mtx_lock(&g_eventlock);
98	KASSERT(!(pp->flags & G_PF_ORPHAN),
99	    ("g_orphan_provider(%p(%s)), already an orphan", pp, pp->name));
100	pp->flags |= G_PF_ORPHAN;
101	TAILQ_INSERT_TAIL(&g_doorstep, pp, orphan);
102	mtx_unlock(&g_eventlock);
103	wakeup(&g_wait_event);
104}
105
106/*
107 * This function is called once on each provider which the event handler
108 * finds on its g_doorstep.
109 */
110
111static void
112g_orphan_register(struct g_provider *pp)
113{
114	struct g_consumer *cp, *cp2;
115	int wf;
116
117	g_topology_assert();
118	G_VALID_PROVIDER(pp);
119	g_trace(G_T_TOPOLOGY, "g_orphan_register(%s)", pp->name);
120
121	wf = pp->flags & G_PF_WITHER;
122	pp->flags &= ~G_PF_WITHER;
123
124	/*
125	 * Tell all consumers the bad news.
126	 * Don't be surprised if they self-destruct.
127	 */
128	cp = LIST_FIRST(&pp->consumers);
129	while (cp != NULL) {
130		cp2 = LIST_NEXT(cp, consumers);
131		KASSERT(cp->geom->orphan != NULL,
132		    ("geom %s has no orphan, class %s",
133		    cp->geom->name, cp->geom->class->name));
134		cp->geom->orphan(cp);
135		cp = cp2;
136	}
137	if (LIST_EMPTY(&pp->consumers) && wf)
138		g_destroy_provider(pp);
139	else
140		pp->flags |= wf;
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 int
151one_event(void)
152{
153	struct g_event *ep;
154	struct g_provider *pp;
155
156	g_topology_lock();
157	for (;;) {
158		mtx_lock(&g_eventlock);
159		pp = TAILQ_FIRST(&g_doorstep);
160		if (pp != NULL) {
161			G_VALID_PROVIDER(pp);
162			TAILQ_REMOVE(&g_doorstep, pp, orphan);
163		}
164		mtx_unlock(&g_eventlock);
165		if (pp == NULL)
166			break;
167		g_orphan_register(pp);
168	}
169	mtx_lock(&g_eventlock);
170	ep = TAILQ_FIRST(&g_events);
171	if (ep == NULL) {
172		mtx_unlock(&g_eventlock);
173		g_topology_unlock();
174		return (0);
175	}
176	TAILQ_REMOVE(&g_events, ep, events);
177	mtx_unlock(&g_eventlock);
178	g_topology_assert();
179	ep->func(ep->arg, 0);
180	g_topology_assert();
181	if (ep->flag & EV_WAKEUP) {
182		ep->flag |= EV_DONE;
183		wakeup(ep);
184	} else {
185		g_free(ep);
186	}
187	g_pending_events--;
188	if (g_pending_events == 0)
189		wakeup(&g_pending_events);
190	g_topology_unlock();
191	return (1);
192}
193
194void
195g_run_events()
196{
197
198	while (one_event())
199		;
200}
201
202void
203g_cancel_event(void *ref)
204{
205	struct g_event *ep, *epn;
206	struct g_provider *pp;
207	u_int n;
208
209	mtx_lock(&g_eventlock);
210	TAILQ_FOREACH(pp, &g_doorstep, orphan) {
211		if (pp != ref)
212			continue;
213		TAILQ_REMOVE(&g_doorstep, pp, orphan);
214		break;
215	}
216	for (ep = TAILQ_FIRST(&g_events); ep != NULL; ep = epn) {
217		epn = TAILQ_NEXT(ep, events);
218		for (n = 0; n < G_N_EVENTREFS; n++) {
219			if (ep->ref[n] == NULL)
220				break;
221			if (ep->ref[n] == ref) {
222				TAILQ_REMOVE(&g_events, ep, events);
223				ep->func(ep->arg, EV_CANCEL);
224				if (ep->flag & EV_WAKEUP) {
225					ep->flag |= EV_DONE;
226					ep->flag |= EV_CANCELED;
227					wakeup(ep);
228				} else {
229					g_free(ep);
230				}
231				if (--g_pending_events == 0)
232					wakeup(&g_pending_events);
233				break;
234			}
235		}
236	}
237	mtx_unlock(&g_eventlock);
238}
239
240static int
241g_post_event_x(g_event_t *func, void *arg, int flag, int wuflag, struct g_event **epp, va_list ap)
242{
243	struct g_event *ep;
244	void *p;
245	u_int n;
246
247	g_trace(G_T_TOPOLOGY, "g_post_event_x(%p, %p, %d, %d)",
248	    func, arg, flag, wakeup);
249	KASSERT(wuflag == 0 || wuflag == EV_WAKEUP,
250	    ("Wrong wuflag in g_post_event_x(0x%x)", wuflag));
251	ep = g_malloc(sizeof *ep, flag | M_ZERO);
252	if (ep == NULL)
253		return (ENOMEM);
254	ep->flag = wuflag;
255	for (n = 0; n < G_N_EVENTREFS; n++) {
256		p = va_arg(ap, void *);
257		if (p == NULL)
258			break;
259		g_trace(G_T_TOPOLOGY, "  ref %p", p);
260		ep->ref[n] = p;
261	}
262	KASSERT(p == NULL, ("Too many references to event"));
263	ep->func = func;
264	ep->arg = arg;
265	mtx_lock(&g_eventlock);
266	g_pending_events++;
267	TAILQ_INSERT_TAIL(&g_events, ep, events);
268	mtx_unlock(&g_eventlock);
269	wakeup(&g_wait_event);
270	if (epp != NULL)
271		*epp = ep;
272	return (0);
273}
274
275int
276g_post_event(g_event_t *func, void *arg, int flag, ...)
277{
278	va_list ap;
279	int i;
280
281	KASSERT(flag == M_WAITOK || flag == M_NOWAIT,
282	    ("Wrong flag to g_post_event"));
283	va_start(ap, flag);
284	i = g_post_event_x(func, arg, flag, 0, NULL, ap);
285	va_end(ap);
286	return (i);
287}
288
289
290/*
291 * XXX: It might actually be useful to call this function with topology held.
292 * XXX: This would ensure that the event gets created before anything else
293 * XXX: changes.  At present all users have a handle on things in some other
294 * XXX: way, so this remains an XXX for now.
295 */
296
297int
298g_waitfor_event(g_event_t *func, void *arg, int flag, ...)
299{
300	va_list ap;
301	struct g_event *ep;
302	int error;
303
304	g_topology_assert_not();
305	KASSERT(flag == M_WAITOK || flag == M_NOWAIT,
306	    ("Wrong flag to g_post_event"));
307	va_start(ap, flag);
308	error = g_post_event_x(func, arg, flag, EV_WAKEUP, &ep, ap);
309	va_end(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_free(ep);
318	return (error);
319}
320
321void
322g_event_init()
323{
324
325	mtx_init(&g_eventlock, "GEOM orphanage", NULL, MTX_DEF);
326}
327