kern_hhook.c revision 251725
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
7220560Slstewart * for Advanced Internet Architectures, Swinburne University of Technology,
8220560Slstewart * made possible in part by grants from the FreeBSD Foundation and Cisco
9220560Slstewart * University Research 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 251725 2013-06-14 02:25:40Z 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
61220592Spluknetstatic MALLOC_DEFINE(M_HHOOK, "hhook", "Helper hooks are linked off hhook_head lists");
62216615Slstewart
63216615SlstewartLIST_HEAD(hhookheadhead, hhook_head);
64216615SlstewartVNET_DEFINE(struct hhookheadhead, hhook_head_list);
65216615Slstewart#define	V_hhook_head_list VNET(hhook_head_list)
66216615Slstewart
67216615Slstewartstatic struct mtx hhook_head_list_lock;
68216615SlstewartMTX_SYSINIT(hhookheadlistlock, &hhook_head_list_lock, "hhook_head list lock",
69216615Slstewart    MTX_DEF);
70216615Slstewart
71216615Slstewart/* Private function prototypes. */
72216615Slstewartstatic void hhook_head_destroy(struct hhook_head *hhh);
73216615Slstewart
74216615Slstewart#define	HHHLIST_LOCK() mtx_lock(&hhook_head_list_lock)
75216615Slstewart#define	HHHLIST_UNLOCK() mtx_unlock(&hhook_head_list_lock)
76216615Slstewart#define	HHHLIST_LOCK_ASSERT() mtx_assert(&hhook_head_list_lock, MA_OWNED)
77216615Slstewart
78216615Slstewart#define	HHH_LOCK_INIT(hhh) rm_init(&(hhh)->hhh_lock, "hhook_head rm lock")
79216615Slstewart#define	HHH_LOCK_DESTROY(hhh) rm_destroy(&(hhh)->hhh_lock)
80216615Slstewart#define	HHH_WLOCK(hhh) rm_wlock(&(hhh)->hhh_lock)
81216615Slstewart#define	HHH_WUNLOCK(hhh) rm_wunlock(&(hhh)->hhh_lock)
82216615Slstewart#define	HHH_RLOCK(hhh, rmpt) rm_rlock(&(hhh)->hhh_lock, (rmpt))
83216615Slstewart#define	HHH_RUNLOCK(hhh, rmpt) rm_runlock(&(hhh)->hhh_lock, (rmpt))
84216615Slstewart
85216615Slstewart/*
86216615Slstewart * Run all helper hook functions for a given hook point.
87216615Slstewart */
88216615Slstewartvoid
89216615Slstewarthhook_run_hooks(struct hhook_head *hhh, void *ctx_data, struct osd *hosd)
90216615Slstewart{
91216615Slstewart	struct hhook *hhk;
92216615Slstewart	void *hdata;
93216615Slstewart	struct rm_priotracker rmpt;
94216615Slstewart
95216615Slstewart	KASSERT(hhh->hhh_refcount > 0, ("hhook_head %p refcount is 0", hhh));
96216615Slstewart
97216615Slstewart	HHH_RLOCK(hhh, &rmpt);
98216615Slstewart	STAILQ_FOREACH(hhk, &hhh->hhh_hooks, hhk_next) {
99216615Slstewart		if (hhk->hhk_helper->h_flags & HELPER_NEEDS_OSD) {
100216615Slstewart			hdata = osd_get(OSD_KHELP, hosd, hhk->hhk_helper->h_id);
101216615Slstewart			if (hdata == NULL)
102216615Slstewart				continue;
103216615Slstewart		} else
104216615Slstewart			hdata = NULL;
105216615Slstewart
106216615Slstewart		/*
107216615Slstewart		 * XXXLAS: We currently ignore the int returned by the hook,
108216615Slstewart		 * but will likely want to handle it in future to allow hhook to
109216615Slstewart		 * be used like pfil and effect changes at the hhook calling
110216615Slstewart		 * site e.g. we could define a new hook type of HHOOK_TYPE_PFIL
111216615Slstewart		 * and standardise what particular return values mean and set
112216615Slstewart		 * the context data to pass exactly the same information as pfil
113216615Slstewart		 * hooks currently receive, thus replicating pfil with hhook.
114216615Slstewart		 */
115216615Slstewart		hhk->hhk_func(hhh->hhh_type, hhh->hhh_id, hhk->hhk_udata,
116216615Slstewart		    ctx_data, hdata, hosd);
117216615Slstewart	}
118216615Slstewart	HHH_RUNLOCK(hhh, &rmpt);
119216615Slstewart}
120216615Slstewart
121216615Slstewart/*
122216615Slstewart * Register a new helper hook function with a helper hook point.
123216615Slstewart */
124216615Slstewartint
125216615Slstewarthhook_add_hook(struct hhook_head *hhh, struct hookinfo *hki, uint32_t flags)
126216615Slstewart{
127216615Slstewart	struct hhook *hhk, *tmp;
128216615Slstewart	int error;
129216615Slstewart
130216615Slstewart	error = 0;
131216615Slstewart
132216615Slstewart	if (hhh == NULL)
133216615Slstewart		return (ENOENT);
134216615Slstewart
135216615Slstewart	hhk = malloc(sizeof(struct hhook), M_HHOOK,
136216615Slstewart	    M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
137216615Slstewart
138216615Slstewart	if (hhk == NULL)
139216615Slstewart		return (ENOMEM);
140216615Slstewart
141216615Slstewart	hhk->hhk_helper = hki->hook_helper;
142216615Slstewart	hhk->hhk_func = hki->hook_func;
143216615Slstewart	hhk->hhk_udata = hki->hook_udata;
144216615Slstewart
145216615Slstewart	HHH_WLOCK(hhh);
146216615Slstewart	STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) {
147216615Slstewart		if (tmp->hhk_func == hki->hook_func &&
148216615Slstewart		    tmp->hhk_udata == hki->hook_udata) {
149216615Slstewart			/* The helper hook function is already registered. */
150216615Slstewart			error = EEXIST;
151216615Slstewart			break;
152216615Slstewart		}
153216615Slstewart	}
154216615Slstewart
155216615Slstewart	if (!error) {
156216615Slstewart		STAILQ_INSERT_TAIL(&hhh->hhh_hooks, hhk, hhk_next);
157216615Slstewart		hhh->hhh_nhooks++;
158217248Slstewart	} else
159216615Slstewart		free(hhk, M_HHOOK);
160216615Slstewart
161216615Slstewart	HHH_WUNLOCK(hhh);
162216615Slstewart
163216615Slstewart	return (error);
164216615Slstewart}
165216615Slstewart
166216615Slstewart/*
167216615Slstewart * Lookup a helper hook point and register a new helper hook function with it.
168216615Slstewart */
169216615Slstewartint
170216615Slstewarthhook_add_hook_lookup(struct hookinfo *hki, uint32_t flags)
171216615Slstewart{
172216615Slstewart	struct hhook_head *hhh;
173216615Slstewart	int error;
174216615Slstewart
175216615Slstewart	hhh = hhook_head_get(hki->hook_type, hki->hook_id);
176216615Slstewart
177216615Slstewart	if (hhh == NULL)
178216615Slstewart		return (ENOENT);
179216615Slstewart
180216615Slstewart	error = hhook_add_hook(hhh, hki, flags);
181216615Slstewart	hhook_head_release(hhh);
182216615Slstewart
183216615Slstewart	return (error);
184216615Slstewart}
185216615Slstewart
186216615Slstewart/*
187216615Slstewart * Remove a helper hook function from a helper hook point.
188216615Slstewart */
189216615Slstewartint
190216615Slstewarthhook_remove_hook(struct hhook_head *hhh, struct hookinfo *hki)
191216615Slstewart{
192216615Slstewart	struct hhook *tmp;
193216615Slstewart
194216615Slstewart	if (hhh == NULL)
195216615Slstewart		return (ENOENT);
196216615Slstewart
197216615Slstewart	HHH_WLOCK(hhh);
198216615Slstewart	STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) {
199216615Slstewart		if (tmp->hhk_func == hki->hook_func &&
200216615Slstewart		    tmp->hhk_udata == hki->hook_udata) {
201216615Slstewart			STAILQ_REMOVE(&hhh->hhh_hooks, tmp, hhook, hhk_next);
202216615Slstewart			free(tmp, M_HHOOK);
203216615Slstewart			hhh->hhh_nhooks--;
204216615Slstewart			break;
205216615Slstewart		}
206216615Slstewart	}
207216615Slstewart	HHH_WUNLOCK(hhh);
208216615Slstewart
209216615Slstewart	return (0);
210216615Slstewart}
211216615Slstewart
212216615Slstewart/*
213216615Slstewart * Lookup a helper hook point and remove a helper hook function from it.
214216615Slstewart */
215216615Slstewartint
216216615Slstewarthhook_remove_hook_lookup(struct hookinfo *hki)
217216615Slstewart{
218216615Slstewart	struct hhook_head *hhh;
219216615Slstewart
220216615Slstewart	hhh = hhook_head_get(hki->hook_type, hki->hook_id);
221216615Slstewart
222216615Slstewart	if (hhh == NULL)
223216615Slstewart		return (ENOENT);
224216615Slstewart
225216615Slstewart	hhook_remove_hook(hhh, hki);
226216615Slstewart	hhook_head_release(hhh);
227216615Slstewart
228216615Slstewart	return (0);
229216615Slstewart}
230216615Slstewart
231216615Slstewart/*
232216615Slstewart * Register a new helper hook point.
233216615Slstewart */
234216615Slstewartint
235216615Slstewarthhook_head_register(int32_t hhook_type, int32_t hhook_id, struct hhook_head **hhh,
236216615Slstewart    uint32_t flags)
237216615Slstewart{
238216615Slstewart	struct hhook_head *tmphhh;
239216615Slstewart
240216615Slstewart	tmphhh = hhook_head_get(hhook_type, hhook_id);
241216615Slstewart
242216615Slstewart	if (tmphhh != NULL) {
243216615Slstewart		/* Hook point previously registered. */
244216615Slstewart		hhook_head_release(tmphhh);
245216615Slstewart		return (EEXIST);
246216615Slstewart	}
247216615Slstewart
248216615Slstewart	/* XXXLAS: Need to implement support for non-virtualised hooks. */
249216615Slstewart	if ((flags & HHOOK_HEADISINVNET) == 0) {
250216615Slstewart		printf("%s: only vnet-style virtualised hooks can be used\n",
251216615Slstewart		    __func__);
252216615Slstewart		return (EINVAL);
253216615Slstewart	}
254216615Slstewart
255216615Slstewart	tmphhh = malloc(sizeof(struct hhook_head), M_HHOOK,
256216615Slstewart	    M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
257216615Slstewart
258216615Slstewart	if (tmphhh == NULL)
259216615Slstewart		return (ENOMEM);
260216615Slstewart
261216615Slstewart	tmphhh->hhh_type = hhook_type;
262216615Slstewart	tmphhh->hhh_id = hhook_id;
263216615Slstewart	tmphhh->hhh_nhooks = 0;
264216615Slstewart	STAILQ_INIT(&tmphhh->hhh_hooks);
265216615Slstewart	HHH_LOCK_INIT(tmphhh);
266216615Slstewart
267251725Slstewart	if (hhh != NULL) {
268216615Slstewart		refcount_init(&tmphhh->hhh_refcount, 1);
269251725Slstewart		*hhh = tmphhh;
270251725Slstewart	} else
271216615Slstewart		refcount_init(&tmphhh->hhh_refcount, 0);
272216615Slstewart
273216615Slstewart	if (flags & HHOOK_HEADISINVNET) {
274216615Slstewart		tmphhh->hhh_flags |= HHH_ISINVNET;
275216615Slstewart		HHHLIST_LOCK();
276216615Slstewart		LIST_INSERT_HEAD(&V_hhook_head_list, tmphhh, hhh_next);
277216615Slstewart		HHHLIST_UNLOCK();
278216615Slstewart	} else {
279216615Slstewart		/* XXXLAS: Add tmphhh to the non-virtualised list. */
280216615Slstewart	}
281216615Slstewart
282216615Slstewart	return (0);
283216615Slstewart}
284216615Slstewart
285216615Slstewartstatic void
286216615Slstewarthhook_head_destroy(struct hhook_head *hhh)
287216615Slstewart{
288216615Slstewart	struct hhook *tmp, *tmp2;
289216615Slstewart
290216615Slstewart	HHHLIST_LOCK_ASSERT();
291216615Slstewart
292216615Slstewart	LIST_REMOVE(hhh, hhh_next);
293216615Slstewart	HHH_WLOCK(hhh);
294216615Slstewart	STAILQ_FOREACH_SAFE(tmp, &hhh->hhh_hooks, hhk_next, tmp2)
295216615Slstewart		free(tmp, M_HHOOK);
296216615Slstewart	HHH_WUNLOCK(hhh);
297216615Slstewart	HHH_LOCK_DESTROY(hhh);
298216615Slstewart	free(hhh, M_HHOOK);
299216615Slstewart}
300216615Slstewart
301216615Slstewart/*
302216615Slstewart * Remove a helper hook point.
303216615Slstewart */
304216615Slstewartint
305216615Slstewarthhook_head_deregister(struct hhook_head *hhh)
306216615Slstewart{
307216615Slstewart	int error;
308216615Slstewart
309216615Slstewart	error = 0;
310216615Slstewart
311216615Slstewart	HHHLIST_LOCK();
312216615Slstewart	if (hhh == NULL)
313216615Slstewart		error = ENOENT;
314216615Slstewart	else if (hhh->hhh_refcount > 1)
315216615Slstewart		error = EBUSY;
316216615Slstewart	else
317216615Slstewart		hhook_head_destroy(hhh);
318216615Slstewart	HHHLIST_UNLOCK();
319216615Slstewart
320216615Slstewart	return (error);
321216615Slstewart}
322216615Slstewart
323216615Slstewart/*
324216615Slstewart * Remove a helper hook point via a hhook_head lookup.
325216615Slstewart */
326216615Slstewartint
327216615Slstewarthhook_head_deregister_lookup(int32_t hhook_type, int32_t hhook_id)
328216615Slstewart{
329216615Slstewart	struct hhook_head *hhh;
330216615Slstewart	int error;
331216615Slstewart
332216615Slstewart	hhh = hhook_head_get(hhook_type, hhook_id);
333216615Slstewart	error = hhook_head_deregister(hhh);
334216615Slstewart
335216615Slstewart	if (error == EBUSY)
336216615Slstewart		hhook_head_release(hhh);
337216615Slstewart
338216615Slstewart	return (error);
339216615Slstewart}
340216615Slstewart
341216615Slstewart/*
342216615Slstewart * Lookup and return the hhook_head struct associated with the specified type
343216615Slstewart * and id, or NULL if not found. If found, the hhook_head's refcount is bumped.
344216615Slstewart */
345216615Slstewartstruct hhook_head *
346216615Slstewarthhook_head_get(int32_t hhook_type, int32_t hhook_id)
347216615Slstewart{
348216615Slstewart	struct hhook_head *hhh;
349216615Slstewart
350216615Slstewart	/* XXXLAS: Pick hhook_head_list based on hhook_head flags. */
351216615Slstewart	HHHLIST_LOCK();
352216615Slstewart	LIST_FOREACH(hhh, &V_hhook_head_list, hhh_next) {
353216615Slstewart		if (hhh->hhh_type == hhook_type && hhh->hhh_id == hhook_id) {
354216615Slstewart			refcount_acquire(&hhh->hhh_refcount);
355217248Slstewart			break;
356216615Slstewart		}
357216615Slstewart	}
358216615Slstewart	HHHLIST_UNLOCK();
359216615Slstewart
360217248Slstewart	return (hhh);
361216615Slstewart}
362216615Slstewart
363216615Slstewartvoid
364216615Slstewarthhook_head_release(struct hhook_head *hhh)
365216615Slstewart{
366216615Slstewart
367216615Slstewart	refcount_release(&hhh->hhh_refcount);
368216615Slstewart}
369216615Slstewart
370216615Slstewart/*
371216615Slstewart * Check the hhook_head private flags and return the appropriate public
372216615Slstewart * representation of the flag to the caller. The function is implemented in a
373216615Slstewart * way that allows us to cope with other subsystems becoming virtualised in the
374216615Slstewart * future.
375216615Slstewart */
376216615Slstewartuint32_t
377216615Slstewarthhook_head_is_virtualised(struct hhook_head *hhh)
378216615Slstewart{
379216615Slstewart	uint32_t ret;
380216615Slstewart
381217250Slstewart	ret = 0;
382216615Slstewart
383217250Slstewart	if (hhh != NULL) {
384217250Slstewart		if (hhh->hhh_flags & HHH_ISINVNET)
385217250Slstewart			ret = HHOOK_HEADISINVNET;
386217250Slstewart	}
387216615Slstewart
388216615Slstewart	return (ret);
389216615Slstewart}
390216615Slstewart
391216615Slstewartuint32_t
392216615Slstewarthhook_head_is_virtualised_lookup(int32_t hook_type, int32_t hook_id)
393216615Slstewart{
394216615Slstewart	struct hhook_head *hhh;
395216615Slstewart	uint32_t ret;
396216615Slstewart
397216615Slstewart	hhh = hhook_head_get(hook_type, hook_id);
398216615Slstewart
399216615Slstewart	if (hhh == NULL)
400216615Slstewart		return (0);
401216615Slstewart
402216615Slstewart	ret = hhook_head_is_virtualised(hhh);
403216615Slstewart	hhook_head_release(hhh);
404216615Slstewart
405216615Slstewart	return (ret);
406216615Slstewart}
407216615Slstewart
408216615Slstewart/*
409216615Slstewart * Vnet created and being initialised.
410216615Slstewart */
411216615Slstewartstatic void
412216615Slstewarthhook_vnet_init(const void *unused __unused)
413216615Slstewart{
414216615Slstewart
415216615Slstewart	LIST_INIT(&V_hhook_head_list);
416216615Slstewart}
417216615Slstewart
418216615Slstewart/*
419216615Slstewart * Vnet being torn down and destroyed.
420216615Slstewart */
421216615Slstewartstatic void
422216615Slstewarthhook_vnet_uninit(const void *unused __unused)
423216615Slstewart{
424216615Slstewart	struct hhook_head *hhh, *tmphhh;
425216615Slstewart
426216615Slstewart	/*
427216615Slstewart	 * If subsystems which export helper hook points use the hhook KPI
428216615Slstewart	 * correctly, the loop below should have no work to do because the
429216615Slstewart	 * subsystem should have already called hhook_head_deregister().
430216615Slstewart	 */
431216615Slstewart	HHHLIST_LOCK();
432216615Slstewart	LIST_FOREACH_SAFE(hhh, &V_hhook_head_list, hhh_next, tmphhh) {
433216615Slstewart		printf("%s: hhook_head type=%d, id=%d cleanup required\n",
434216615Slstewart		    __func__, hhh->hhh_type, hhh->hhh_id);
435216615Slstewart		hhook_head_destroy(hhh);
436216615Slstewart	}
437216615Slstewart	HHHLIST_UNLOCK();
438216615Slstewart}
439216615Slstewart
440216615Slstewart
441216615Slstewart/*
442216615Slstewart * When a vnet is created and being initialised, init the V_hhook_head_list.
443216615Slstewart */
444216615SlstewartVNET_SYSINIT(hhook_vnet_init, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST,
445216615Slstewart    hhook_vnet_init, NULL);
446216615Slstewart
447216615Slstewart/*
448216615Slstewart * The hhook KPI provides a mechanism for subsystems which export helper hook
449216615Slstewart * points to clean up on vnet tear down, but in case the KPI is misused,
450216615Slstewart * provide a function to clean up and free memory for a vnet being destroyed.
451216615Slstewart */
452216615SlstewartVNET_SYSUNINIT(hhook_vnet_uninit, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST,
453216615Slstewart    hhook_vnet_uninit, NULL);
454