kern_hhook.c revision 216615
1216615Slstewart/*-
2216615Slstewart * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
3216615Slstewart * Copyright (c) 2010 The FreeBSD Foundation
4216615Slstewart * All rights reserved.
5216615Slstewart *
6216615Slstewart * This software was developed by Lawrence Stewart while studying at the Centre
7216615Slstewart * for Advanced Internet Architectures, Swinburne University, made possible in
8216615Slstewart * part by grants from the FreeBSD Foundation and Cisco University Research
9216615Slstewart * Program Fund at Community Foundation Silicon Valley.
10216615Slstewart *
11216615Slstewart * Portions of this software were developed at the Centre for Advanced
12216615Slstewart * Internet Architectures, Swinburne University of Technology, Melbourne,
13216615Slstewart * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation.
14216615Slstewart *
15216615Slstewart * Redistribution and use in source and binary forms, with or without
16216615Slstewart * modification, are permitted provided that the following conditions
17216615Slstewart * are met:
18216615Slstewart * 1. Redistributions of source code must retain the above copyright
19216615Slstewart *    notice, this list of conditions and the following disclaimer.
20216615Slstewart * 2. Redistributions in binary form must reproduce the above copyright
21216615Slstewart *    notice, this list of conditions and the following disclaimer in the
22216615Slstewart *    documentation and/or other materials provided with the distribution.
23216615Slstewart *
24216615Slstewart * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25216615Slstewart * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26216615Slstewart * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27216615Slstewart * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28216615Slstewart * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29216615Slstewart * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30216615Slstewart * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31216615Slstewart * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32216615Slstewart * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33216615Slstewart * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34216615Slstewart * SUCH DAMAGE.
35216615Slstewart */
36216615Slstewart
37216615Slstewart#include <sys/cdefs.h>
38216615Slstewart__FBSDID("$FreeBSD: head/sys/kern/kern_hhook.c 216615 2010-12-21 13:45:29Z lstewart $");
39216615Slstewart
40216615Slstewart#include <sys/param.h>
41216615Slstewart#include <sys/kernel.h>
42216615Slstewart#include <sys/hhook.h>
43216615Slstewart#include <sys/khelp.h>
44216615Slstewart#include <sys/malloc.h>
45216615Slstewart#include <sys/module.h>
46216615Slstewart#include <sys/module_khelp.h>
47216615Slstewart#include <sys/osd.h>
48216615Slstewart#include <sys/queue.h>
49216615Slstewart#include <sys/refcount.h>
50216615Slstewart#include <sys/systm.h>
51216615Slstewart
52216615Slstewart#include <net/vnet.h>
53216615Slstewart
54216615Slstewartstruct hhook {
55216615Slstewart	hhook_func_t		hhk_func;
56216615Slstewart	struct helper		*hhk_helper;
57216615Slstewart	void			*hhk_udata;
58216615Slstewart	STAILQ_ENTRY(hhook)	hhk_next;
59216615Slstewart};
60216615Slstewart
61216615SlstewartMALLOC_DECLARE(M_HHOOK);
62216615SlstewartMALLOC_DEFINE(M_HHOOK, "hhook", "Helper hooks are linked off hhook_head lists");
63216615Slstewart
64216615SlstewartLIST_HEAD(hhookheadhead, hhook_head);
65216615SlstewartVNET_DEFINE(struct hhookheadhead, hhook_head_list);
66216615Slstewart#define	V_hhook_head_list VNET(hhook_head_list)
67216615Slstewart
68216615Slstewartstatic struct mtx hhook_head_list_lock;
69216615SlstewartMTX_SYSINIT(hhookheadlistlock, &hhook_head_list_lock, "hhook_head list lock",
70216615Slstewart    MTX_DEF);
71216615Slstewart
72216615Slstewart/* Private function prototypes. */
73216615Slstewartstatic void hhook_head_destroy(struct hhook_head *hhh);
74216615Slstewart
75216615Slstewart#define	HHHLIST_LOCK() mtx_lock(&hhook_head_list_lock)
76216615Slstewart#define	HHHLIST_UNLOCK() mtx_unlock(&hhook_head_list_lock)
77216615Slstewart#define	HHHLIST_LOCK_ASSERT() mtx_assert(&hhook_head_list_lock, MA_OWNED)
78216615Slstewart
79216615Slstewart#define	HHH_LOCK_INIT(hhh) rm_init(&(hhh)->hhh_lock, "hhook_head rm lock")
80216615Slstewart#define	HHH_LOCK_DESTROY(hhh) rm_destroy(&(hhh)->hhh_lock)
81216615Slstewart#define	HHH_WLOCK(hhh) rm_wlock(&(hhh)->hhh_lock)
82216615Slstewart#define	HHH_WUNLOCK(hhh) rm_wunlock(&(hhh)->hhh_lock)
83216615Slstewart#define	HHH_RLOCK(hhh, rmpt) rm_rlock(&(hhh)->hhh_lock, (rmpt))
84216615Slstewart#define	HHH_RUNLOCK(hhh, rmpt) rm_runlock(&(hhh)->hhh_lock, (rmpt))
85216615Slstewart
86216615Slstewart/*
87216615Slstewart * Run all helper hook functions for a given hook point.
88216615Slstewart */
89216615Slstewartvoid
90216615Slstewarthhook_run_hooks(struct hhook_head *hhh, void *ctx_data, struct osd *hosd)
91216615Slstewart{
92216615Slstewart	struct hhook *hhk;
93216615Slstewart	void *hdata;
94216615Slstewart	struct rm_priotracker rmpt;
95216615Slstewart
96216615Slstewart	KASSERT(hhh->hhh_refcount > 0, ("hhook_head %p refcount is 0", hhh));
97216615Slstewart
98216615Slstewart	HHH_RLOCK(hhh, &rmpt);
99216615Slstewart	STAILQ_FOREACH(hhk, &hhh->hhh_hooks, hhk_next) {
100216615Slstewart		if (hhk->hhk_helper->h_flags & HELPER_NEEDS_OSD) {
101216615Slstewart			hdata = osd_get(OSD_KHELP, hosd, hhk->hhk_helper->h_id);
102216615Slstewart			if (hdata == NULL)
103216615Slstewart				continue;
104216615Slstewart		} else
105216615Slstewart			hdata = NULL;
106216615Slstewart
107216615Slstewart		/*
108216615Slstewart		 * XXXLAS: We currently ignore the int returned by the hook,
109216615Slstewart		 * but will likely want to handle it in future to allow hhook to
110216615Slstewart		 * be used like pfil and effect changes at the hhook calling
111216615Slstewart		 * site e.g. we could define a new hook type of HHOOK_TYPE_PFIL
112216615Slstewart		 * and standardise what particular return values mean and set
113216615Slstewart		 * the context data to pass exactly the same information as pfil
114216615Slstewart		 * hooks currently receive, thus replicating pfil with hhook.
115216615Slstewart		 */
116216615Slstewart		hhk->hhk_func(hhh->hhh_type, hhh->hhh_id, hhk->hhk_udata,
117216615Slstewart		    ctx_data, hdata, hosd);
118216615Slstewart	}
119216615Slstewart	HHH_RUNLOCK(hhh, &rmpt);
120216615Slstewart}
121216615Slstewart
122216615Slstewart/*
123216615Slstewart * Register a new helper hook function with a helper hook point.
124216615Slstewart */
125216615Slstewartint
126216615Slstewarthhook_add_hook(struct hhook_head *hhh, struct hookinfo *hki, uint32_t flags)
127216615Slstewart{
128216615Slstewart	struct hhook *hhk, *tmp;
129216615Slstewart	int error;
130216615Slstewart
131216615Slstewart	error = 0;
132216615Slstewart
133216615Slstewart	if (hhh == NULL)
134216615Slstewart		return (ENOENT);
135216615Slstewart
136216615Slstewart	hhk = malloc(sizeof(struct hhook), M_HHOOK,
137216615Slstewart	    M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
138216615Slstewart
139216615Slstewart	if (hhk == NULL)
140216615Slstewart		return (ENOMEM);
141216615Slstewart
142216615Slstewart	hhk->hhk_helper = hki->hook_helper;
143216615Slstewart	hhk->hhk_func = hki->hook_func;
144216615Slstewart	hhk->hhk_udata = hki->hook_udata;
145216615Slstewart
146216615Slstewart	HHH_WLOCK(hhh);
147216615Slstewart	STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) {
148216615Slstewart		if (tmp->hhk_func == hki->hook_func &&
149216615Slstewart		    tmp->hhk_udata == hki->hook_udata) {
150216615Slstewart			/* The helper hook function is already registered. */
151216615Slstewart			error = EEXIST;
152216615Slstewart			break;
153216615Slstewart		}
154216615Slstewart	}
155216615Slstewart
156216615Slstewart	if (!error) {
157216615Slstewart		STAILQ_INSERT_TAIL(&hhh->hhh_hooks, hhk, hhk_next);
158216615Slstewart		hhh->hhh_nhooks++;
159216615Slstewart	}
160216615Slstewart	else
161216615Slstewart		free(hhk, M_HHOOK);
162216615Slstewart
163216615Slstewart	HHH_WUNLOCK(hhh);
164216615Slstewart
165216615Slstewart	return (error);
166216615Slstewart}
167216615Slstewart
168216615Slstewart/*
169216615Slstewart * Lookup a helper hook point and register a new helper hook function with it.
170216615Slstewart */
171216615Slstewartint
172216615Slstewarthhook_add_hook_lookup(struct hookinfo *hki, uint32_t flags)
173216615Slstewart{
174216615Slstewart	struct hhook_head *hhh;
175216615Slstewart	int error;
176216615Slstewart
177216615Slstewart	hhh = hhook_head_get(hki->hook_type, hki->hook_id);
178216615Slstewart
179216615Slstewart	if (hhh == NULL)
180216615Slstewart		return (ENOENT);
181216615Slstewart
182216615Slstewart	error = hhook_add_hook(hhh, hki, flags);
183216615Slstewart	hhook_head_release(hhh);
184216615Slstewart
185216615Slstewart	return (error);
186216615Slstewart}
187216615Slstewart
188216615Slstewart/*
189216615Slstewart * Remove a helper hook function from a helper hook point.
190216615Slstewart */
191216615Slstewartint
192216615Slstewarthhook_remove_hook(struct hhook_head *hhh, struct hookinfo *hki)
193216615Slstewart{
194216615Slstewart	struct hhook *tmp;
195216615Slstewart
196216615Slstewart	if (hhh == NULL)
197216615Slstewart		return (ENOENT);
198216615Slstewart
199216615Slstewart	HHH_WLOCK(hhh);
200216615Slstewart	STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) {
201216615Slstewart		if (tmp->hhk_func == hki->hook_func &&
202216615Slstewart		    tmp->hhk_udata == hki->hook_udata) {
203216615Slstewart			STAILQ_REMOVE(&hhh->hhh_hooks, tmp, hhook, hhk_next);
204216615Slstewart			free(tmp, M_HHOOK);
205216615Slstewart			hhh->hhh_nhooks--;
206216615Slstewart			break;
207216615Slstewart		}
208216615Slstewart	}
209216615Slstewart	HHH_WUNLOCK(hhh);
210216615Slstewart
211216615Slstewart	return (0);
212216615Slstewart}
213216615Slstewart
214216615Slstewart/*
215216615Slstewart * Lookup a helper hook point and remove a helper hook function from it.
216216615Slstewart */
217216615Slstewartint
218216615Slstewarthhook_remove_hook_lookup(struct hookinfo *hki)
219216615Slstewart{
220216615Slstewart	struct hhook_head *hhh;
221216615Slstewart
222216615Slstewart	hhh = hhook_head_get(hki->hook_type, hki->hook_id);
223216615Slstewart
224216615Slstewart	if (hhh == NULL)
225216615Slstewart		return (ENOENT);
226216615Slstewart
227216615Slstewart	hhook_remove_hook(hhh, hki);
228216615Slstewart	hhook_head_release(hhh);
229216615Slstewart
230216615Slstewart	return (0);
231216615Slstewart}
232216615Slstewart
233216615Slstewart/*
234216615Slstewart * Register a new helper hook point.
235216615Slstewart */
236216615Slstewartint
237216615Slstewarthhook_head_register(int32_t hhook_type, int32_t hhook_id, struct hhook_head **hhh,
238216615Slstewart    uint32_t flags)
239216615Slstewart{
240216615Slstewart	struct hhook_head *tmphhh;
241216615Slstewart
242216615Slstewart	tmphhh = hhook_head_get(hhook_type, hhook_id);
243216615Slstewart
244216615Slstewart	if (tmphhh != NULL) {
245216615Slstewart		/* Hook point previously registered. */
246216615Slstewart		hhook_head_release(tmphhh);
247216615Slstewart		return (EEXIST);
248216615Slstewart	}
249216615Slstewart
250216615Slstewart	/* XXXLAS: Need to implement support for non-virtualised hooks. */
251216615Slstewart	if ((flags & HHOOK_HEADISINVNET) == 0) {
252216615Slstewart		printf("%s: only vnet-style virtualised hooks can be used\n",
253216615Slstewart		    __func__);
254216615Slstewart		return (EINVAL);
255216615Slstewart	}
256216615Slstewart
257216615Slstewart	tmphhh = malloc(sizeof(struct hhook_head), M_HHOOK,
258216615Slstewart	    M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
259216615Slstewart
260216615Slstewart	if (tmphhh == NULL)
261216615Slstewart		return (ENOMEM);
262216615Slstewart
263216615Slstewart	tmphhh->hhh_type = hhook_type;
264216615Slstewart	tmphhh->hhh_id = hhook_id;
265216615Slstewart	tmphhh->hhh_nhooks = 0;
266216615Slstewart	STAILQ_INIT(&tmphhh->hhh_hooks);
267216615Slstewart	HHH_LOCK_INIT(tmphhh);
268216615Slstewart
269216615Slstewart	if (hhh != NULL)
270216615Slstewart		refcount_init(&tmphhh->hhh_refcount, 1);
271216615Slstewart	else
272216615Slstewart		refcount_init(&tmphhh->hhh_refcount, 0);
273216615Slstewart
274216615Slstewart	if (flags & HHOOK_HEADISINVNET) {
275216615Slstewart		tmphhh->hhh_flags |= HHH_ISINVNET;
276216615Slstewart		HHHLIST_LOCK();
277216615Slstewart		LIST_INSERT_HEAD(&V_hhook_head_list, tmphhh, hhh_next);
278216615Slstewart		HHHLIST_UNLOCK();
279216615Slstewart	} else {
280216615Slstewart		/* XXXLAS: Add tmphhh to the non-virtualised list. */
281216615Slstewart	}
282216615Slstewart
283216615Slstewart	*hhh = tmphhh;
284216615Slstewart
285216615Slstewart	return (0);
286216615Slstewart}
287216615Slstewart
288216615Slstewartstatic void
289216615Slstewarthhook_head_destroy(struct hhook_head *hhh)
290216615Slstewart{
291216615Slstewart	struct hhook *tmp, *tmp2;
292216615Slstewart
293216615Slstewart	HHHLIST_LOCK_ASSERT();
294216615Slstewart
295216615Slstewart	LIST_REMOVE(hhh, hhh_next);
296216615Slstewart	HHH_WLOCK(hhh);
297216615Slstewart	STAILQ_FOREACH_SAFE(tmp, &hhh->hhh_hooks, hhk_next, tmp2)
298216615Slstewart		free(tmp, M_HHOOK);
299216615Slstewart	HHH_WUNLOCK(hhh);
300216615Slstewart	HHH_LOCK_DESTROY(hhh);
301216615Slstewart	free(hhh, M_HHOOK);
302216615Slstewart}
303216615Slstewart
304216615Slstewart/*
305216615Slstewart * Remove a helper hook point.
306216615Slstewart */
307216615Slstewartint
308216615Slstewarthhook_head_deregister(struct hhook_head *hhh)
309216615Slstewart{
310216615Slstewart	int error;
311216615Slstewart
312216615Slstewart	error = 0;
313216615Slstewart
314216615Slstewart	HHHLIST_LOCK();
315216615Slstewart	if (hhh == NULL)
316216615Slstewart		error = ENOENT;
317216615Slstewart	else if (hhh->hhh_refcount > 1)
318216615Slstewart		error = EBUSY;
319216615Slstewart	else
320216615Slstewart		hhook_head_destroy(hhh);
321216615Slstewart	HHHLIST_UNLOCK();
322216615Slstewart
323216615Slstewart	return (error);
324216615Slstewart}
325216615Slstewart
326216615Slstewart/*
327216615Slstewart * Remove a helper hook point via a hhook_head lookup.
328216615Slstewart */
329216615Slstewartint
330216615Slstewarthhook_head_deregister_lookup(int32_t hhook_type, int32_t hhook_id)
331216615Slstewart{
332216615Slstewart	struct hhook_head *hhh;
333216615Slstewart	int error;
334216615Slstewart
335216615Slstewart	error = 0;
336216615Slstewart	hhh = hhook_head_get(hhook_type, hhook_id);
337216615Slstewart	error = hhook_head_deregister(hhh);
338216615Slstewart
339216615Slstewart	if (error == EBUSY)
340216615Slstewart		hhook_head_release(hhh);
341216615Slstewart
342216615Slstewart	return (error);
343216615Slstewart}
344216615Slstewart
345216615Slstewart/*
346216615Slstewart * Lookup and return the hhook_head struct associated with the specified type
347216615Slstewart * and id, or NULL if not found. If found, the hhook_head's refcount is bumped.
348216615Slstewart */
349216615Slstewartstruct hhook_head *
350216615Slstewarthhook_head_get(int32_t hhook_type, int32_t hhook_id)
351216615Slstewart{
352216615Slstewart	struct hhook_head *hhh;
353216615Slstewart
354216615Slstewart	/* XXXLAS: Pick hhook_head_list based on hhook_head flags. */
355216615Slstewart	HHHLIST_LOCK();
356216615Slstewart	LIST_FOREACH(hhh, &V_hhook_head_list, hhh_next) {
357216615Slstewart		if (hhh->hhh_type == hhook_type && hhh->hhh_id == hhook_id) {
358216615Slstewart			refcount_acquire(&hhh->hhh_refcount);
359216615Slstewart			HHHLIST_UNLOCK();
360216615Slstewart			return (hhh);
361216615Slstewart		}
362216615Slstewart	}
363216615Slstewart	HHHLIST_UNLOCK();
364216615Slstewart
365216615Slstewart	return (NULL);
366216615Slstewart}
367216615Slstewart
368216615Slstewartvoid
369216615Slstewarthhook_head_release(struct hhook_head *hhh)
370216615Slstewart{
371216615Slstewart
372216615Slstewart	refcount_release(&hhh->hhh_refcount);
373216615Slstewart}
374216615Slstewart
375216615Slstewart/*
376216615Slstewart * Check the hhook_head private flags and return the appropriate public
377216615Slstewart * representation of the flag to the caller. The function is implemented in a
378216615Slstewart * way that allows us to cope with other subsystems becoming virtualised in the
379216615Slstewart * future.
380216615Slstewart */
381216615Slstewartuint32_t
382216615Slstewarthhook_head_is_virtualised(struct hhook_head *hhh)
383216615Slstewart{
384216615Slstewart	uint32_t ret;
385216615Slstewart
386216615Slstewart	if (hhh == NULL)
387216615Slstewart		return (0);
388216615Slstewart
389216615Slstewart	if (hhh->hhh_flags & HHH_ISINVNET)
390216615Slstewart		ret = HHOOK_HEADISINVNET;
391216615Slstewart
392216615Slstewart	return (ret);
393216615Slstewart}
394216615Slstewart
395216615Slstewartuint32_t
396216615Slstewarthhook_head_is_virtualised_lookup(int32_t hook_type, int32_t hook_id)
397216615Slstewart{
398216615Slstewart	struct hhook_head *hhh;
399216615Slstewart	uint32_t ret;
400216615Slstewart
401216615Slstewart	hhh = hhook_head_get(hook_type, hook_id);
402216615Slstewart
403216615Slstewart	if (hhh == NULL)
404216615Slstewart		return (0);
405216615Slstewart
406216615Slstewart	ret = hhook_head_is_virtualised(hhh);
407216615Slstewart	hhook_head_release(hhh);
408216615Slstewart
409216615Slstewart	return (ret);
410216615Slstewart}
411216615Slstewart
412216615Slstewart/*
413216615Slstewart * Vnet created and being initialised.
414216615Slstewart */
415216615Slstewartstatic void
416216615Slstewarthhook_vnet_init(const void *unused __unused)
417216615Slstewart{
418216615Slstewart
419216615Slstewart	LIST_INIT(&V_hhook_head_list);
420216615Slstewart}
421216615Slstewart
422216615Slstewart/*
423216615Slstewart * Vnet being torn down and destroyed.
424216615Slstewart */
425216615Slstewartstatic void
426216615Slstewarthhook_vnet_uninit(const void *unused __unused)
427216615Slstewart{
428216615Slstewart	struct hhook_head *hhh, *tmphhh;
429216615Slstewart
430216615Slstewart	/*
431216615Slstewart	 * If subsystems which export helper hook points use the hhook KPI
432216615Slstewart	 * correctly, the loop below should have no work to do because the
433216615Slstewart	 * subsystem should have already called hhook_head_deregister().
434216615Slstewart	 */
435216615Slstewart	HHHLIST_LOCK();
436216615Slstewart	LIST_FOREACH_SAFE(hhh, &V_hhook_head_list, hhh_next, tmphhh) {
437216615Slstewart		printf("%s: hhook_head type=%d, id=%d cleanup required\n",
438216615Slstewart		    __func__, hhh->hhh_type, hhh->hhh_id);
439216615Slstewart		hhook_head_destroy(hhh);
440216615Slstewart	}
441216615Slstewart	HHHLIST_UNLOCK();
442216615Slstewart}
443216615Slstewart
444216615Slstewart
445216615Slstewart/*
446216615Slstewart * When a vnet is created and being initialised, init the V_hhook_head_list.
447216615Slstewart */
448216615SlstewartVNET_SYSINIT(hhook_vnet_init, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST,
449216615Slstewart    hhook_vnet_init, NULL);
450216615Slstewart
451216615Slstewart/*
452216615Slstewart * The hhook KPI provides a mechanism for subsystems which export helper hook
453216615Slstewart * points to clean up on vnet tear down, but in case the KPI is misused,
454216615Slstewart * provide a function to clean up and free memory for a vnet being destroyed.
455216615Slstewart */
456216615SlstewartVNET_SYSUNINIT(hhook_vnet_uninit, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST,
457216615Slstewart    hhook_vnet_uninit, NULL);
458