ng_base.c revision 338333
1139823Simp/*-
252419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
352419Sjulian * All rights reserved.
470700Sjulian *
552419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
652419Sjulian * redistribution of this software, in source or object code forms, with or
752419Sjulian * without modifications are expressly permitted by Whistle Communications;
852419Sjulian * provided, however, that:
952419Sjulian * 1. Any and all reproductions of the source or object code must include the
1052419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1152419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1252419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1352419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1452419Sjulian *    such appears in the above copyright notice or in the software.
1570700Sjulian *
1652419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
1752419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
1852419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
1952419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2052419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2152419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2252419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2352419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2452419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2552419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2652419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
2752419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
2852419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
2952419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3052419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3152419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3252419Sjulian * OF SUCH DAMAGE.
3352419Sjulian *
3467506Sjulian * Authors: Julian Elischer <julian@freebsd.org>
3567506Sjulian *          Archie Cobbs <archie@freebsd.org>
3652419Sjulian *
3752419Sjulian * $FreeBSD: stable/11/sys/netgraph/ng_base.c 338333 2018-08-27 03:42:19Z mav $
3852419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
3952419Sjulian */
4052419Sjulian
4152419Sjulian/*
4252419Sjulian * This file implements the base netgraph code.
4352419Sjulian */
4452419Sjulian
4552419Sjulian#include <sys/param.h>
46231766Sglebius#include <sys/systm.h>
47139235Sglebius#include <sys/ctype.h>
48231831Sglebius#include <sys/hash.h>
49131933Smarcel#include <sys/kdb.h>
5052419Sjulian#include <sys/kernel.h>
51231760Sglebius#include <sys/kthread.h>
52154225Sglebius#include <sys/ktr.h>
53114216Skan#include <sys/limits.h>
54230480Sglebius#include <sys/lock.h>
5552419Sjulian#include <sys/malloc.h>
56139235Sglebius#include <sys/mbuf.h>
57231760Sglebius#include <sys/proc.h>
5852419Sjulian#include <sys/queue.h>
59231760Sglebius#include <sys/refcount.h>
60231760Sglebius#include <sys/rwlock.h>
61231760Sglebius#include <sys/smp.h>
6272946Sjulian#include <sys/sysctl.h>
63139235Sglebius#include <sys/syslog.h>
64186093Smav#include <sys/unistd.h>
65177953Smav#include <machine/cpu.h>
66295126Sglebius#include <vm/uma.h>
6752419Sjulian
6852419Sjulian#include <net/netisr.h>
69195699Srwatson#include <net/vnet.h>
7052419Sjulian
7152419Sjulian#include <netgraph/ng_message.h>
7252419Sjulian#include <netgraph/netgraph.h>
7353913Sarchie#include <netgraph/ng_parse.h>
7452419Sjulian
7572053SjulianMODULE_VERSION(netgraph, NG_ABI_VERSION);
7659756Speter
77151974Sglebius/* Mutex to protect topology events. */
78256550Smelifarostatic struct rwlock	ng_topo_lock;
79256550Smelifaro#define	TOPOLOGY_RLOCK()	rw_rlock(&ng_topo_lock)
80256550Smelifaro#define	TOPOLOGY_RUNLOCK()	rw_runlock(&ng_topo_lock)
81256550Smelifaro#define	TOPOLOGY_WLOCK()	rw_wlock(&ng_topo_lock)
82256550Smelifaro#define	TOPOLOGY_WUNLOCK()	rw_wunlock(&ng_topo_lock)
83256550Smelifaro#define	TOPOLOGY_NOTOWNED()	rw_assert(&ng_topo_lock, RA_UNLOCKED)
84151974Sglebius
8570784Sjulian#ifdef	NETGRAPH_DEBUG
86176802Smavstatic struct mtx	ng_nodelist_mtx; /* protects global node/hook lists */
87152451Sglebiusstatic struct mtx	ngq_mtx;	/* protects the queue item list */
8870784Sjulian
8970784Sjulianstatic SLIST_HEAD(, ng_node) ng_allnodes;
9070784Sjulianstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
9170784Sjulianstatic SLIST_HEAD(, ng_hook) ng_allhooks;
9270784Sjulianstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
9370784Sjulian
9470784Sjulianstatic void ng_dumpitems(void);
9570784Sjulianstatic void ng_dumpnodes(void);
9670784Sjulianstatic void ng_dumphooks(void);
9770784Sjulian
9870784Sjulian#endif	/* NETGRAPH_DEBUG */
9970935Sjulian/*
100152451Sglebius * DEAD versions of the structures.
101249583Sgabor * In order to avoid races, it is sometimes necessary to point
102152451Sglebius * at SOMETHING even though theoretically, the current entity is
10370935Sjulian * INVALID. Use these to avoid these races.
10470935Sjulian */
10570935Sjulianstruct ng_type ng_deadtype = {
10670935Sjulian	NG_ABI_VERSION,
10770935Sjulian	"dead",
10870935Sjulian	NULL,	/* modevent */
10970935Sjulian	NULL,	/* constructor */
11070935Sjulian	NULL,	/* rcvmsg */
11170935Sjulian	NULL,	/* shutdown */
11270935Sjulian	NULL,	/* newhook */
11370935Sjulian	NULL,	/* findhook */
11470935Sjulian	NULL,	/* connect */
11570935Sjulian	NULL,	/* rcvdata */
11670935Sjulian	NULL,	/* disconnect */
11770935Sjulian	NULL, 	/* cmdlist */
11870935Sjulian};
11970784Sjulian
12070935Sjulianstruct ng_node ng_deadnode = {
12170935Sjulian	"dead",
12270935Sjulian	&ng_deadtype,
123132464Sjulian	NGF_INVALID,
12470935Sjulian	0,	/* numhooks */
12570935Sjulian	NULL,	/* private */
12670935Sjulian	0,	/* ID */
127201145Santoine	LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks),
12870935Sjulian	{},	/* all_nodes list entry */
12970935Sjulian	{},	/* id hashtable list entry */
13070935Sjulian	{	0,
131178228Smav		0,
13270935Sjulian		{}, /* should never use! (should hang) */
133178228Smav		{}, /* workqueue entry */
134178228Smav		STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
13570935Sjulian	},
136178228Smav	1,	/* refs */
137191827Szec	NULL,	/* vnet */
13870935Sjulian#ifdef	NETGRAPH_DEBUG
13970935Sjulian	ND_MAGIC,
14070935Sjulian	__FILE__,
14170935Sjulian	__LINE__,
14270935Sjulian	{NULL}
14370935Sjulian#endif	/* NETGRAPH_DEBUG */
14470935Sjulian};
14570935Sjulian
14670935Sjulianstruct ng_hook ng_deadhook = {
14770935Sjulian	"dead",
14870935Sjulian	NULL,		/* private */
14970935Sjulian	HK_INVALID | HK_DEAD,
150148261Sglebius	0,		/* undefined data link type */
15170935Sjulian	&ng_deadhook,	/* Peer is self */
15270935Sjulian	&ng_deadnode,	/* attached to deadnode */
15370935Sjulian	{},		/* hooks list */
15471885Sjulian	NULL,		/* override rcvmsg() */
15571885Sjulian	NULL,		/* override rcvdata() */
156178228Smav	1,		/* refs always >= 1 */
15770935Sjulian#ifdef	NETGRAPH_DEBUG
15870935Sjulian	HK_MAGIC,
15970935Sjulian	__FILE__,
16070935Sjulian	__LINE__,
16170935Sjulian	{NULL}
16270935Sjulian#endif	/* NETGRAPH_DEBUG */
16370935Sjulian};
16470935Sjulian
16570935Sjulian/*
16670935Sjulian * END DEAD STRUCTURES
16770935Sjulian */
16870700Sjulian/* List nodes with unallocated work */
169178228Smavstatic STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
17071902Sjulianstatic struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
17170700Sjulian
17252419Sjulian/* List of installed types */
17370700Sjulianstatic LIST_HEAD(, ng_type) ng_typelist;
174230480Sglebiusstatic struct rwlock	ng_typelist_lock;
175230480Sglebius#define	TYPELIST_RLOCK()	rw_rlock(&ng_typelist_lock)
176230480Sglebius#define	TYPELIST_RUNLOCK()	rw_runlock(&ng_typelist_lock)
177230480Sglebius#define	TYPELIST_WLOCK()	rw_wlock(&ng_typelist_lock)
178230480Sglebius#define	TYPELIST_WUNLOCK()	rw_wunlock(&ng_typelist_lock)
17952419Sjulian
180231831Sglebius/* Hash related definitions. */
181231831SglebiusLIST_HEAD(nodehash, ng_node);
182231831Sglebiusstatic VNET_DEFINE(struct nodehash *, ng_ID_hash);
183231831Sglebiusstatic VNET_DEFINE(u_long, ng_ID_hmask);
184231831Sglebiusstatic VNET_DEFINE(u_long, ng_nodes);
185231831Sglebiusstatic VNET_DEFINE(struct nodehash *, ng_name_hash);
186231831Sglebiusstatic VNET_DEFINE(u_long, ng_name_hmask);
187231831Sglebiusstatic VNET_DEFINE(u_long, ng_named_nodes);
188231831Sglebius#define	V_ng_ID_hash		VNET(ng_ID_hash)
189231831Sglebius#define	V_ng_ID_hmask		VNET(ng_ID_hmask)
190231831Sglebius#define	V_ng_nodes		VNET(ng_nodes)
191231831Sglebius#define	V_ng_name_hash		VNET(ng_name_hash)
192231831Sglebius#define	V_ng_name_hmask		VNET(ng_name_hmask)
193231831Sglebius#define	V_ng_named_nodes	VNET(ng_named_nodes)
194195699Srwatson
195230480Sglebiusstatic struct rwlock	ng_idhash_lock;
196230480Sglebius#define	IDHASH_RLOCK()		rw_rlock(&ng_idhash_lock)
197230480Sglebius#define	IDHASH_RUNLOCK()	rw_runlock(&ng_idhash_lock)
198230480Sglebius#define	IDHASH_WLOCK()		rw_wlock(&ng_idhash_lock)
199230480Sglebius#define	IDHASH_WUNLOCK()	rw_wunlock(&ng_idhash_lock)
200230480Sglebius
20171354Sjulian/* Method to find a node.. used twice so do it here */
202231831Sglebius#define NG_IDHASH_FN(ID) ((ID) % (V_ng_ID_hmask + 1))
20371354Sjulian#define NG_IDHASH_FIND(ID, node)					\
20471354Sjulian	do { 								\
205230480Sglebius		rw_assert(&ng_idhash_lock, RA_LOCKED);			\
206181803Sbz		LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)],	\
20771354Sjulian						nd_idnodes) {		\
20871354Sjulian			if (NG_NODE_IS_VALID(node)			\
20971354Sjulian			&& (NG_NODE_ID(node) == ID)) {			\
21071354Sjulian				break;					\
21171354Sjulian			}						\
21271354Sjulian		}							\
21371354Sjulian	} while (0)
21452722Sjulian
215230480Sglebiusstatic struct rwlock	ng_namehash_lock;
216230480Sglebius#define	NAMEHASH_RLOCK()	rw_rlock(&ng_namehash_lock)
217230480Sglebius#define	NAMEHASH_RUNLOCK()	rw_runlock(&ng_namehash_lock)
218230480Sglebius#define	NAMEHASH_WLOCK()	rw_wlock(&ng_namehash_lock)
219230480Sglebius#define	NAMEHASH_WUNLOCK()	rw_wunlock(&ng_namehash_lock)
220176802Smav
22152419Sjulian/* Internal functions */
22252419Sjulianstatic int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
22370700Sjulianstatic int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
22452722Sjulianstatic ng_ID_t	ng_decodeidname(const char *name);
22552419Sjulianstatic int	ngb_mod_event(module_t mod, int event, void *data);
226177953Smavstatic void	ng_worklist_add(node_p node);
227186093Smavstatic void	ngthread(void *);
228170180Sglebiusstatic int	ng_apply_item(node_p node, item_p item, int rw);
229178228Smavstatic void	ng_flush_input_queue(node_p node);
23070700Sjulianstatic node_p	ng_ID2noderef(ng_ID_t ID);
231172806Smavstatic int	ng_con_nodes(item_p item, node_p node, const char *name,
232172806Smav		    node_p node2, const char *name2);
233172806Smavstatic int	ng_con_part2(node_p node, item_p item, hook_p hook);
234172806Smavstatic int	ng_con_part3(node_p node, item_p item, hook_p hook);
235231831Sglebiusstatic int	ng_mkpeer(node_p node, const char *name, const char *name2,
236231831Sglebius		    char *type);
237231831Sglebiusstatic void	ng_name_rehash(void);
238231831Sglebiusstatic void	ng_ID_rehash(void);
23952419Sjulian
240152451Sglebius/* Imported, these used to be externally visible, some may go back. */
24170700Sjulianvoid	ng_destroy_hook(hook_p hook);
24270700Sjulianint	ng_path2noderef(node_p here, const char *path,
24370700Sjulian	node_p *dest, hook_p *lasthook);
24470700Sjulianint	ng_make_node(const char *type, node_p *nodepp);
24570700Sjulianint	ng_path_parse(char *addr, char **node, char **path, char **hook);
24671849Sjulianvoid	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
24770784Sjulianvoid	ng_unname(node_p node);
24870700Sjulian
24952419Sjulian/* Our own netgraph malloc type */
25052419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
25170700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
252227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook",
253227293Sed    "netgraph hook structures");
254227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node",
255227293Sed    "netgraph node structures");
256227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item",
257227293Sed    "netgraph item structures");
25852419Sjulian
25970700Sjulian/* Should not be visible outside this file */
26070784Sjulian
26170784Sjulian#define _NG_ALLOC_HOOK(hook) \
262184205Sdes	hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
26370784Sjulian#define _NG_ALLOC_NODE(node) \
264184205Sdes	node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
26570784Sjulian
266168049Swkoszek#define	NG_QUEUE_LOCK_INIT(n)			\
267168137Swkoszek	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
268168049Swkoszek#define	NG_QUEUE_LOCK(n)			\
269168137Swkoszek	mtx_lock(&(n)->q_mtx)
270168049Swkoszek#define	NG_QUEUE_UNLOCK(n)			\
271168137Swkoszek	mtx_unlock(&(n)->q_mtx)
272168049Swkoszek#define	NG_WORKLIST_LOCK_INIT()			\
273168137Swkoszek	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
274168049Swkoszek#define	NG_WORKLIST_LOCK()			\
275168137Swkoszek	mtx_lock(&ng_worklist_mtx)
276168049Swkoszek#define	NG_WORKLIST_UNLOCK()			\
277168137Swkoszek	mtx_unlock(&ng_worklist_mtx)
278186093Smav#define	NG_WORKLIST_SLEEP()			\
279186093Smav	mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0)
280186093Smav#define	NG_WORKLIST_WAKEUP()			\
281186093Smav	wakeup_one(&ng_worklist)
282168049Swkoszek
28370784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
28470784Sjulian/*
28570784Sjulian * In debug mode:
28670784Sjulian * In an attempt to help track reference count screwups
28770784Sjulian * we do not free objects back to the malloc system, but keep them
28870784Sjulian * in a local cache where we can examine them and keep information safely
28970784Sjulian * after they have been freed.
29070784Sjulian * We use this scheme for nodes and hooks, and to some extent for items.
29170784Sjulian */
29270784Sjulianstatic __inline hook_p
29370784Sjulianng_alloc_hook(void)
29470784Sjulian{
29570784Sjulian	hook_p hook;
29670784Sjulian	SLIST_ENTRY(ng_hook) temp;
29772200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
29870784Sjulian	hook = LIST_FIRST(&ng_freehooks);
29970784Sjulian	if (hook) {
30070784Sjulian		LIST_REMOVE(hook, hk_hooks);
30170784Sjulian		bcopy(&hook->hk_all, &temp, sizeof(temp));
30270784Sjulian		bzero(hook, sizeof(struct ng_hook));
30370784Sjulian		bcopy(&temp, &hook->hk_all, sizeof(temp));
30472200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
30570784Sjulian		hook->hk_magic = HK_MAGIC;
30670784Sjulian	} else {
30772200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
30870784Sjulian		_NG_ALLOC_HOOK(hook);
30970784Sjulian		if (hook) {
31070784Sjulian			hook->hk_magic = HK_MAGIC;
31172200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
31270784Sjulian			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
31372200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
31470784Sjulian		}
31570784Sjulian	}
31670784Sjulian	return (hook);
31770784Sjulian}
31870784Sjulian
31970784Sjulianstatic __inline node_p
32070784Sjulianng_alloc_node(void)
32170784Sjulian{
32270784Sjulian	node_p node;
32370784Sjulian	SLIST_ENTRY(ng_node) temp;
32472200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
32570784Sjulian	node = LIST_FIRST(&ng_freenodes);
32670784Sjulian	if (node) {
32770784Sjulian		LIST_REMOVE(node, nd_nodes);
32870784Sjulian		bcopy(&node->nd_all, &temp, sizeof(temp));
32970784Sjulian		bzero(node, sizeof(struct ng_node));
33070784Sjulian		bcopy(&temp, &node->nd_all, sizeof(temp));
33172200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
33270784Sjulian		node->nd_magic = ND_MAGIC;
33370784Sjulian	} else {
33472200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
33570784Sjulian		_NG_ALLOC_NODE(node);
33670784Sjulian		if (node) {
33770784Sjulian			node->nd_magic = ND_MAGIC;
33872200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
33970784Sjulian			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
34072200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
34170784Sjulian		}
34270784Sjulian	}
34370784Sjulian	return (node);
34470784Sjulian}
34570784Sjulian
34670784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
34770784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
34870784Sjulian
34970784Sjulian#define NG_FREE_HOOK(hook)						\
35070784Sjulian	do {								\
351229003Sglebius		mtx_lock(&ng_nodelist_mtx);				\
35270784Sjulian		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
35370784Sjulian		hook->hk_magic = 0;					\
354229003Sglebius		mtx_unlock(&ng_nodelist_mtx);				\
35570784Sjulian	} while (0)
35670784Sjulian
35770784Sjulian#define NG_FREE_NODE(node)						\
35870784Sjulian	do {								\
359229003Sglebius		mtx_lock(&ng_nodelist_mtx);				\
36070784Sjulian		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
36170784Sjulian		node->nd_magic = 0;					\
362229003Sglebius		mtx_unlock(&ng_nodelist_mtx);				\
36370784Sjulian	} while (0)
36470784Sjulian
36570784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
36670784Sjulian
36770784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
36870784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
36970784Sjulian
370184205Sdes#define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0)
371184205Sdes#define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0)
37270784Sjulian
37370784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
37470784Sjulian
375131933Smarcel/* Set this to kdb_enter("X") to catch all errors as they occur */
37652419Sjulian#ifndef TRAP_ERROR
37771047Sjulian#define TRAP_ERROR()
37852419Sjulian#endif
37952419Sjulian
380215701Sdimstatic VNET_DEFINE(ng_ID_t, nextID) = 1;
381195727Srwatson#define	V_nextID			VNET(nextID)
38252722Sjulian
38353403Sarchie#ifdef INVARIANTS
38453403Sarchie#define CHECK_DATA_MBUF(m)	do {					\
38553403Sarchie		struct mbuf *n;						\
38653403Sarchie		int total;						\
38753403Sarchie									\
388113255Sdes		M_ASSERTPKTHDR(m);					\
389149818Sglebius		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
39053403Sarchie			total += n->m_len;				\
391149818Sglebius			if (n->m_nextpkt != NULL)			\
392149818Sglebius				panic("%s: m_nextpkt", __func__);	\
393149818Sglebius		}							\
394149827Sglebius									\
39553403Sarchie		if ((m)->m_pkthdr.len != total) {			\
39653403Sarchie			panic("%s: %d != %d",				\
39787599Sobrien			    __func__, (m)->m_pkthdr.len, total);	\
39853403Sarchie		}							\
39953403Sarchie	} while (0)
40053403Sarchie#else
40153403Sarchie#define CHECK_DATA_MBUF(m)
40253403Sarchie#endif
40352722Sjulian
404172806Smav#define ERROUT(x)	do { error = (x); goto done; } while (0)
40553403Sarchie
40652419Sjulian/************************************************************************
40753913Sarchie	Parse type definitions for generic messages
40853913Sarchie************************************************************************/
40953913Sarchie
41053913Sarchie/* Handy structure parse type defining macro */
41153913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
41297685Sarchiestatic const struct ng_parse_struct_field				\
41397685Sarchie	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
41453913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
41553913Sarchie	&ng_parse_struct_type,						\
41697685Sarchie	&ng_ ## lo ## _type_fields					\
41753913Sarchie}
41853913Sarchie
41953913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
42053913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
42153913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
42253913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
42353913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
42453913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
42553913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
42653913Sarchie
42753913Sarchie/* Get length of an array when the length is stored as a 32 bit
42872645Sasmodai   value immediately preceding the array -- as with struct namelist
42953913Sarchie   and struct typelist. */
43053913Sarchiestatic int
43153913Sarchieng_generic_list_getLength(const struct ng_parse_type *type,
43253913Sarchie	const u_char *start, const u_char *buf)
43353913Sarchie{
43453913Sarchie	return *((const u_int32_t *)(buf - 4));
43553913Sarchie}
43653913Sarchie
43753913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */
43853913Sarchiestatic int
43953913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type,
44053913Sarchie	const u_char *start, const u_char *buf)
44153913Sarchie{
44253913Sarchie	const struct hooklist *hl = (const struct hooklist *)start;
44353913Sarchie
44453913Sarchie	return hl->nodeinfo.hooks;
44553913Sarchie}
44653913Sarchie
44753913Sarchie/* Array type for a variable length array of struct namelist */
44853913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
44953913Sarchie	&ng_generic_nodeinfo_type,
45053913Sarchie	&ng_generic_list_getLength
45153913Sarchie};
45253913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = {
45353913Sarchie	&ng_parse_array_type,
45453913Sarchie	&ng_nodeinfoarray_type_info
45553913Sarchie};
45653913Sarchie
45753913Sarchie/* Array type for a variable length array of struct typelist */
45853913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = {
45953913Sarchie	&ng_generic_typeinfo_type,
46053913Sarchie	&ng_generic_list_getLength
46153913Sarchie};
46253913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = {
46353913Sarchie	&ng_parse_array_type,
46453913Sarchie	&ng_typeinfoarray_type_info
46553913Sarchie};
46653913Sarchie
46753913Sarchie/* Array type for array of struct linkinfo in struct hooklist */
46853913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
46953913Sarchie	&ng_generic_linkinfo_type,
47053913Sarchie	&ng_generic_linkinfo_getLength
47153913Sarchie};
47253913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = {
47353913Sarchie	&ng_parse_array_type,
47453913Sarchie	&ng_generic_linkinfo_array_type_info
47553913Sarchie};
47653913Sarchie
477260046SglebiusDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_typeinfoarray_type));
47853913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
47953913Sarchie	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
48053913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
48153913Sarchie	(&ng_generic_nodeinfoarray_type));
48253913Sarchie
48353913Sarchie/* List of commands and how to convert arguments to/from ASCII */
48453913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = {
48553913Sarchie	{
48653913Sarchie	  NGM_GENERIC_COOKIE,
48753913Sarchie	  NGM_SHUTDOWN,
48853913Sarchie	  "shutdown",
48953913Sarchie	  NULL,
49053913Sarchie	  NULL
49153913Sarchie	},
49253913Sarchie	{
49353913Sarchie	  NGM_GENERIC_COOKIE,
49453913Sarchie	  NGM_MKPEER,
49553913Sarchie	  "mkpeer",
49653913Sarchie	  &ng_generic_mkpeer_type,
49753913Sarchie	  NULL
49853913Sarchie	},
49953913Sarchie	{
50053913Sarchie	  NGM_GENERIC_COOKIE,
50153913Sarchie	  NGM_CONNECT,
50253913Sarchie	  "connect",
50353913Sarchie	  &ng_generic_connect_type,
50453913Sarchie	  NULL
50553913Sarchie	},
50653913Sarchie	{
50753913Sarchie	  NGM_GENERIC_COOKIE,
50853913Sarchie	  NGM_NAME,
50953913Sarchie	  "name",
51053913Sarchie	  &ng_generic_name_type,
51153913Sarchie	  NULL
51253913Sarchie	},
51353913Sarchie	{
51453913Sarchie	  NGM_GENERIC_COOKIE,
51553913Sarchie	  NGM_RMHOOK,
51653913Sarchie	  "rmhook",
51753913Sarchie	  &ng_generic_rmhook_type,
51853913Sarchie	  NULL
51953913Sarchie	},
52053913Sarchie	{
52153913Sarchie	  NGM_GENERIC_COOKIE,
52253913Sarchie	  NGM_NODEINFO,
52353913Sarchie	  "nodeinfo",
52453913Sarchie	  NULL,
52553913Sarchie	  &ng_generic_nodeinfo_type
52653913Sarchie	},
52753913Sarchie	{
52853913Sarchie	  NGM_GENERIC_COOKIE,
52953913Sarchie	  NGM_LISTHOOKS,
53053913Sarchie	  "listhooks",
53153913Sarchie	  NULL,
53253913Sarchie	  &ng_generic_hooklist_type
53353913Sarchie	},
53453913Sarchie	{
53553913Sarchie	  NGM_GENERIC_COOKIE,
53653913Sarchie	  NGM_LISTNAMES,
53753913Sarchie	  "listnames",
53853913Sarchie	  NULL,
53953913Sarchie	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
54053913Sarchie	},
54153913Sarchie	{
54253913Sarchie	  NGM_GENERIC_COOKIE,
54353913Sarchie	  NGM_LISTNODES,
54453913Sarchie	  "listnodes",
54553913Sarchie	  NULL,
54653913Sarchie	  &ng_generic_listnodes_type
54753913Sarchie	},
54853913Sarchie	{
54953913Sarchie	  NGM_GENERIC_COOKIE,
55053913Sarchie	  NGM_LISTTYPES,
55153913Sarchie	  "listtypes",
55253913Sarchie	  NULL,
553260046Sglebius	  &ng_generic_typelist_type
55453913Sarchie	},
55553913Sarchie	{
55653913Sarchie	  NGM_GENERIC_COOKIE,
55762471Sphk	  NGM_TEXT_CONFIG,
55862471Sphk	  "textconfig",
55962471Sphk	  NULL,
56062471Sphk	  &ng_parse_string_type
56162471Sphk	},
56262471Sphk	{
56362471Sphk	  NGM_GENERIC_COOKIE,
56453913Sarchie	  NGM_TEXT_STATUS,
56553913Sarchie	  "textstatus",
56653913Sarchie	  NULL,
56753913Sarchie	  &ng_parse_string_type
56853913Sarchie	},
56953913Sarchie	{
57053913Sarchie	  NGM_GENERIC_COOKIE,
57153913Sarchie	  NGM_ASCII2BINARY,
57253913Sarchie	  "ascii2binary",
57353913Sarchie	  &ng_parse_ng_mesg_type,
57453913Sarchie	  &ng_parse_ng_mesg_type
57553913Sarchie	},
57653913Sarchie	{
57753913Sarchie	  NGM_GENERIC_COOKIE,
57853913Sarchie	  NGM_BINARY2ASCII,
57953913Sarchie	  "binary2ascii",
58053913Sarchie	  &ng_parse_ng_mesg_type,
58153913Sarchie	  &ng_parse_ng_mesg_type
58253913Sarchie	},
58353913Sarchie	{ 0 }
58453913Sarchie};
58553913Sarchie
58653913Sarchie/************************************************************************
58752419Sjulian			Node routines
58852419Sjulian************************************************************************/
58952419Sjulian
59052419Sjulian/*
59152419Sjulian * Instantiate a node of the requested type
59252419Sjulian */
59352419Sjulianint
59452419Sjulianng_make_node(const char *typename, node_p *nodepp)
59552419Sjulian{
59652419Sjulian	struct ng_type *type;
59770700Sjulian	int	error;
59852419Sjulian
59952419Sjulian	/* Check that the type makes sense */
60052419Sjulian	if (typename == NULL) {
60171047Sjulian		TRAP_ERROR();
60252419Sjulian		return (EINVAL);
60352419Sjulian	}
60452419Sjulian
605132705Sglebius	/* Locate the node type. If we fail we return. Do not try to load
606132705Sglebius	 * module.
607132705Sglebius	 */
608132705Sglebius	if ((type = ng_findtype(typename)) == NULL)
609132705Sglebius		return (ENXIO);
61052419Sjulian
61170700Sjulian	/*
61270700Sjulian	 * If we have a constructor, then make the node and
61370700Sjulian	 * call the constructor to do type specific initialisation.
61470700Sjulian	 */
61570700Sjulian	if (type->constructor != NULL) {
61670700Sjulian		if ((error = ng_make_node_common(type, nodepp)) == 0) {
617220745Sglebius			if ((error = ((*type->constructor)(*nodepp))) != 0) {
61870784Sjulian				NG_NODE_UNREF(*nodepp);
61970700Sjulian			}
62070700Sjulian		}
62170700Sjulian	} else {
62270700Sjulian		/*
62370700Sjulian		 * Node has no constructor. We cannot ask for one
624167677Srwatson		 * to be made. It must be brought into existence by
62570935Sjulian		 * some external agency. The external agency should
62670700Sjulian		 * call ng_make_node_common() directly to get the
62770700Sjulian		 * netgraph part initialised.
62870700Sjulian		 */
62971047Sjulian		TRAP_ERROR();
63070700Sjulian		error = EINVAL;
63170700Sjulian	}
63270700Sjulian	return (error);
63352419Sjulian}
63452419Sjulian
63552419Sjulian/*
63670700Sjulian * Generic node creation. Called by node initialisation for externally
63770700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ).
63852419Sjulian * The returned node has a reference count of 1.
63952419Sjulian */
64052419Sjulianint
64152419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp)
64252419Sjulian{
64352419Sjulian	node_p node;
64452419Sjulian
64552419Sjulian	/* Require the node type to have been already installed */
64652419Sjulian	if (ng_findtype(type->name) == NULL) {
64771047Sjulian		TRAP_ERROR();
64852419Sjulian		return (EINVAL);
64952419Sjulian	}
65052419Sjulian
65152419Sjulian	/* Make a node and try attach it to the type */
65270784Sjulian	NG_ALLOC_NODE(node);
65352419Sjulian	if (node == NULL) {
65471047Sjulian		TRAP_ERROR();
65552419Sjulian		return (ENOMEM);
65652419Sjulian	}
65770784Sjulian	node->nd_type = type;
658193731Szec#ifdef VIMAGE
659193731Szec	node->nd_vnet = curvnet;
660193731Szec#endif
66170784Sjulian	NG_NODE_REF(node);				/* note reference */
66252419Sjulian	type->refs++;
66352419Sjulian
664168049Swkoszek	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
665178228Smav	STAILQ_INIT(&node->nd_input_queue.queue);
66670784Sjulian	node->nd_input_queue.q_flags = 0;
66752419Sjulian
66852419Sjulian	/* Initialize hook list for new node */
66970784Sjulian	LIST_INIT(&node->nd_hooks);
67052419Sjulian
671231831Sglebius	/* Get an ID and put us in the hash chain. */
672230480Sglebius	IDHASH_WLOCK();
67370784Sjulian	for (;;) { /* wrap protection, even if silly */
67470700Sjulian		node_p node2 = NULL;
675181887Sjulian		node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
67671354Sjulian
67770784Sjulian		/* Is there a problem with the new number? */
67871354Sjulian		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
67971354Sjulian		if ((node->nd_ID != 0) && (node2 == NULL)) {
68070784Sjulian			break;
68170700Sjulian		}
68270784Sjulian	}
683231831Sglebius	V_ng_nodes++;
684231831Sglebius	if (V_ng_nodes * 2 > V_ng_ID_hmask)
685231831Sglebius		ng_ID_rehash();
686229003Sglebius	LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node,
687229003Sglebius	    nd_idnodes);
688230480Sglebius	IDHASH_WUNLOCK();
68952722Sjulian
69052419Sjulian	/* Done */
69152419Sjulian	*nodepp = node;
69252419Sjulian	return (0);
69352419Sjulian}
69452419Sjulian
69552419Sjulian/*
69652419Sjulian * Forceably start the shutdown process on a node. Either call
697167677Srwatson * its shutdown method, or do the default shutdown if there is
69852419Sjulian * no type-specific method.
69952419Sjulian *
700167677Srwatson * We can only be called from a shutdown message, so we know we have
70170939Sjulian * a writer lock, and therefore exclusive access. It also means
70270939Sjulian * that we should not be on the work queue, but we check anyhow.
70370700Sjulian *
70470700Sjulian * Persistent node types must have a type-specific method which
705167677Srwatson * allocates a new node in which case, this one is irretrievably going away,
70670939Sjulian * or cleans up anything it needs, and just makes the node valid again,
707152451Sglebius * in which case we allow the node to survive.
70870939Sjulian *
709167677Srwatson * XXX We need to think of how to tell a persistent node that we
71070939Sjulian * REALLY need to go away because the hardware has gone or we
71170939Sjulian * are rebooting.... etc.
71252419Sjulian */
71352419Sjulianvoid
71471849Sjulianng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
71552419Sjulian{
71670939Sjulian	hook_p hook;
71770939Sjulian
71852419Sjulian	/* Check if it's already shutting down */
719132464Sjulian	if ((node->nd_flags & NGF_CLOSING) != 0)
72052419Sjulian		return;
72152419Sjulian
72271849Sjulian	if (node == &ng_deadnode) {
72371849Sjulian		printf ("shutdown called on deadnode\n");
72471849Sjulian		return;
72571849Sjulian	}
72671849Sjulian
72752419Sjulian	/* Add an extra reference so it doesn't go away during this */
72870784Sjulian	NG_NODE_REF(node);
72952419Sjulian
73070784Sjulian	/*
73170784Sjulian	 * Mark it invalid so any newcomers know not to try use it
73270784Sjulian	 * Also add our own mark so we can't recurse
733132464Sjulian	 * note that NGF_INVALID does not do this as it's also set during
73470784Sjulian	 * creation
73570784Sjulian	 */
736132464Sjulian	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
73752419Sjulian
738129836Sjulian	/* If node has its pre-shutdown method, then call it first*/
739129836Sjulian	if (node->nd_type && node->nd_type->close)
740129836Sjulian		(*node->nd_type->close)(node);
741129836Sjulian
74270939Sjulian	/* Notify all remaining connected nodes to disconnect */
74370939Sjulian	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
74470939Sjulian		ng_destroy_hook(hook);
74570784Sjulian
74670700Sjulian	/*
74770700Sjulian	 * Drain the input queue forceably.
74870784Sjulian	 * it has no hooks so what's it going to do, bleed on someone?
74970784Sjulian	 * Theoretically we came here from a queue entry that was added
75070784Sjulian	 * Just before the queue was closed, so it should be empty anyway.
75171902Sjulian	 * Also removes us from worklist if needed.
75270700Sjulian	 */
753178228Smav	ng_flush_input_queue(node);
75470700Sjulian
75552419Sjulian	/* Ask the type if it has anything to do in this case */
75670784Sjulian	if (node->nd_type && node->nd_type->shutdown) {
75770784Sjulian		(*node->nd_type->shutdown)(node);
75871849Sjulian		if (NG_NODE_IS_VALID(node)) {
75971849Sjulian			/*
76071849Sjulian			 * Well, blow me down if the node code hasn't declared
76171849Sjulian			 * that it doesn't want to die.
762298813Spfg			 * Presumably it is a persistent node.
76371849Sjulian			 * If we REALLY want it to go away,
76471849Sjulian			 *  e.g. hardware going away,
765132464Sjulian			 * Our caller should set NGF_REALLY_DIE in nd_flags.
766152451Sglebius			 */
767132464Sjulian			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
76871849Sjulian			NG_NODE_UNREF(node); /* Assume they still have theirs */
76971849Sjulian			return;
77071849Sjulian		}
77170700Sjulian	} else {				/* do the default thing */
77270784Sjulian		NG_NODE_UNREF(node);
77352419Sjulian	}
77452419Sjulian
77570784Sjulian	ng_unname(node); /* basically a NOP these days */
77670784Sjulian
77770784Sjulian	/*
77870784Sjulian	 * Remove extra reference, possibly the last
77970784Sjulian	 * Possible other holders of references may include
78070784Sjulian	 * timeout callouts, but theoretically the node's supposed to
78170784Sjulian	 * have cancelled them. Possibly hardware dependencies may
78270784Sjulian	 * force a driver to 'linger' with a reference.
78370784Sjulian	 */
78470784Sjulian	NG_NODE_UNREF(node);
78552419Sjulian}
78652419Sjulian
78752419Sjulian/*
78874078Sjulian * Remove a reference to the node, possibly the last.
78974078Sjulian * deadnode always acts as it it were the last.
79052419Sjulian */
791223754Sglebiusvoid
79270784Sjulianng_unref_node(node_p node)
79352419Sjulian{
79471047Sjulian
795223754Sglebius	if (node == &ng_deadnode)
796223754Sglebius		return;
79771047Sjulian
798253346Srodrigc	CURVNET_SET(node->nd_vnet);
799253346Srodrigc
800223754Sglebius	if (refcount_release(&node->nd_refs)) { /* we were the last */
80169519Sjulian
80270784Sjulian		node->nd_type->refs--; /* XXX maybe should get types lock? */
803230480Sglebius		NAMEHASH_WLOCK();
804231831Sglebius		if (NG_NODE_HAS_NAME(node)) {
805231831Sglebius			V_ng_named_nodes--;
806231831Sglebius			LIST_REMOVE(node, nd_nodes);
807231831Sglebius		}
808230480Sglebius		NAMEHASH_WUNLOCK();
80970700Sjulian
810230480Sglebius		IDHASH_WLOCK();
811231831Sglebius		V_ng_nodes--;
81270784Sjulian		LIST_REMOVE(node, nd_idnodes);
813230480Sglebius		IDHASH_WUNLOCK();
81452419Sjulian
81570791Sjulian		mtx_destroy(&node->nd_input_queue.q_mtx);
81670700Sjulian		NG_FREE_NODE(node);
81752419Sjulian	}
818253346Srodrigc	CURVNET_RESTORE();
81952419Sjulian}
82052419Sjulian
82152419Sjulian/************************************************************************
82252722Sjulian			Node ID handling
82352722Sjulian************************************************************************/
82452722Sjulianstatic node_p
82570700Sjulianng_ID2noderef(ng_ID_t ID)
82652722Sjulian{
82770784Sjulian	node_p node;
828231831Sglebius
829230480Sglebius	IDHASH_RLOCK();
83071354Sjulian	NG_IDHASH_FIND(ID, node);
831231831Sglebius	if (node)
83270784Sjulian		NG_NODE_REF(node);
833230480Sglebius	IDHASH_RUNLOCK();
83470784Sjulian	return(node);
83552722Sjulian}
83652722Sjulian
83752722Sjulianng_ID_t
83852722Sjulianng_node2ID(node_p node)
83952722Sjulian{
84070912Sjulian	return (node ? NG_NODE_ID(node) : 0);
84152722Sjulian}
84252722Sjulian
84352722Sjulian/************************************************************************
84452419Sjulian			Node name handling
84552419Sjulian************************************************************************/
84652419Sjulian
84752419Sjulian/*
848229003Sglebius * Assign a node a name.
84952419Sjulian */
85052419Sjulianint
85152419Sjulianng_name_node(node_p node, const char *name)
85252419Sjulian{
853231831Sglebius	uint32_t hash;
85470700Sjulian	node_p node2;
855231831Sglebius	int i;
85652419Sjulian
85752419Sjulian	/* Check the name is valid */
858125028Sharti	for (i = 0; i < NG_NODESIZ; i++) {
85952419Sjulian		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
86052419Sjulian			break;
86152419Sjulian	}
86252419Sjulian	if (i == 0 || name[i] != '\0') {
86371047Sjulian		TRAP_ERROR();
86452419Sjulian		return (EINVAL);
86552419Sjulian	}
86652722Sjulian	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
86771047Sjulian		TRAP_ERROR();
86852419Sjulian		return (EINVAL);
86952419Sjulian	}
87052419Sjulian
871231831Sglebius	NAMEHASH_WLOCK();
872231831Sglebius	if (V_ng_named_nodes * 2 > V_ng_name_hmask)
873231831Sglebius		ng_name_rehash();
87452419Sjulian
875231831Sglebius	hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
876231831Sglebius	/* Check the name isn't already being used. */
877231831Sglebius	LIST_FOREACH(node2, &V_ng_name_hash[hash], nd_nodes)
878231831Sglebius		if (NG_NODE_IS_VALID(node2) &&
879231831Sglebius		    (strcmp(NG_NODE_NAME(node2), name) == 0)) {
880231831Sglebius			NAMEHASH_WUNLOCK();
881231831Sglebius			return (EADDRINUSE);
882231831Sglebius		}
883231831Sglebius
884231831Sglebius	if (NG_NODE_HAS_NAME(node))
885231831Sglebius		LIST_REMOVE(node, nd_nodes);
886231831Sglebius	else
887231831Sglebius		V_ng_named_nodes++;
888231831Sglebius	/* Copy it. */
889125028Sharti	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
890176802Smav	/* Update name hash. */
891181803Sbz	LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
892230480Sglebius	NAMEHASH_WUNLOCK();
893176802Smav
89452419Sjulian	return (0);
89552419Sjulian}
89652419Sjulian
89752419Sjulian/*
89852419Sjulian * Find a node by absolute name. The name should NOT end with ':'
89952419Sjulian * The name "." means "this node" and "[xxx]" means "the node
90052419Sjulian * with ID (ie, at address) xxx".
90152419Sjulian *
90252419Sjulian * Returns the node if found, else NULL.
90370700Sjulian * Eventually should add something faster than a sequential search.
904170035Srwatson * Note it acquires a reference on the node so you can be sure it's still
905170035Srwatson * there.
90652419Sjulian */
90752419Sjuliannode_p
90870700Sjulianng_name2noderef(node_p here, const char *name)
90952419Sjulian{
91052722Sjulian	node_p node;
91152722Sjulian	ng_ID_t temp;
912176802Smav	int	hash;
91352419Sjulian
91452419Sjulian	/* "." means "this node" */
91570700Sjulian	if (strcmp(name, ".") == 0) {
91670784Sjulian		NG_NODE_REF(here);
91770700Sjulian		return(here);
91870700Sjulian	}
91952419Sjulian
92052419Sjulian	/* Check for name-by-ID */
92152722Sjulian	if ((temp = ng_decodeidname(name)) != 0) {
92270700Sjulian		return (ng_ID2noderef(temp));
92352419Sjulian	}
92452419Sjulian
925231831Sglebius	/* Find node by name. */
926231831Sglebius	hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
927230480Sglebius	NAMEHASH_RLOCK();
928230480Sglebius	LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes)
929176802Smav		if (NG_NODE_IS_VALID(node) &&
930176802Smav		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
931230480Sglebius			NG_NODE_REF(node);
93252419Sjulian			break;
93370912Sjulian		}
934230480Sglebius	NAMEHASH_RUNLOCK();
935230480Sglebius
93652419Sjulian	return (node);
93752419Sjulian}
93852419Sjulian
93952419Sjulian/*
940108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the
94152722Sjulian * string is not valid, otherwise returns the value.
94252419Sjulian */
94352722Sjulianstatic ng_ID_t
94452419Sjulianng_decodeidname(const char *name)
94552419Sjulian{
94652816Sarchie	const int len = strlen(name);
94753648Sarchie	char *eptr;
94852816Sarchie	u_long val;
94952419Sjulian
95052816Sarchie	/* Check for proper length, brackets, no leading junk */
951229003Sglebius	if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') ||
952229003Sglebius	    (!isxdigit(name[1])))
95370912Sjulian		return ((ng_ID_t)0);
95452419Sjulian
95552816Sarchie	/* Decode number */
95652816Sarchie	val = strtoul(name + 1, &eptr, 16);
957229003Sglebius	if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0))
95853042Sjulian		return ((ng_ID_t)0);
959229003Sglebius
960229003Sglebius	return ((ng_ID_t)val);
96152419Sjulian}
96252419Sjulian
96352419Sjulian/*
96452419Sjulian * Remove a name from a node. This should only be called
96552419Sjulian * when shutting down and removing the node.
96652419Sjulian */
96752419Sjulianvoid
96852419Sjulianng_unname(node_p node)
96952419Sjulian{
97052419Sjulian}
97152419Sjulian
972231831Sglebius/*
973231831Sglebius * Allocate a bigger name hash.
974231831Sglebius */
975231831Sglebiusstatic void
976231831Sglebiusng_name_rehash()
977231831Sglebius{
978231831Sglebius	struct nodehash *new;
979231831Sglebius	uint32_t hash;
980231831Sglebius	u_long hmask;
981231831Sglebius	node_p node, node2;
982231831Sglebius	int i;
983231831Sglebius
984231831Sglebius	new = hashinit_flags((V_ng_name_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
985231831Sglebius	    HASH_NOWAIT);
986231831Sglebius	if (new == NULL)
987231831Sglebius		return;
988231831Sglebius
989231831Sglebius	for (i = 0; i <= V_ng_name_hmask; i++)
990231831Sglebius		LIST_FOREACH_SAFE(node, &V_ng_name_hash[i], nd_nodes, node2) {
991231831Sglebius#ifdef INVARIANTS
992231831Sglebius			LIST_REMOVE(node, nd_nodes);
993231831Sglebius#endif
994231831Sglebius			hash = hash32_str(NG_NODE_NAME(node), HASHINIT) & hmask;
995231831Sglebius			LIST_INSERT_HEAD(&new[hash], node, nd_nodes);
996231831Sglebius		}
997231831Sglebius
998231831Sglebius	hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
999231831Sglebius	V_ng_name_hash = new;
1000231831Sglebius	V_ng_name_hmask = hmask;
1001231831Sglebius}
1002231831Sglebius
1003231831Sglebius/*
1004231831Sglebius * Allocate a bigger ID hash.
1005231831Sglebius */
1006231831Sglebiusstatic void
1007231831Sglebiusng_ID_rehash()
1008231831Sglebius{
1009231831Sglebius	struct nodehash *new;
1010231831Sglebius	uint32_t hash;
1011231831Sglebius	u_long hmask;
1012231831Sglebius	node_p node, node2;
1013231831Sglebius	int i;
1014231831Sglebius
1015231831Sglebius	new = hashinit_flags((V_ng_ID_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
1016231831Sglebius	    HASH_NOWAIT);
1017231831Sglebius	if (new == NULL)
1018231831Sglebius		return;
1019231831Sglebius
1020231831Sglebius	for (i = 0; i <= V_ng_ID_hmask; i++)
1021231831Sglebius		LIST_FOREACH_SAFE(node, &V_ng_ID_hash[i], nd_idnodes, node2) {
1022231831Sglebius#ifdef INVARIANTS
1023231831Sglebius			LIST_REMOVE(node, nd_idnodes);
1024231831Sglebius#endif
1025231831Sglebius			hash = (node->nd_ID % (hmask + 1));
1026231831Sglebius			LIST_INSERT_HEAD(&new[hash], node, nd_idnodes);
1027231831Sglebius		}
1028231831Sglebius
1029231831Sglebius	hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
1030231831Sglebius	V_ng_ID_hash = new;
1031231831Sglebius	V_ng_ID_hmask = hmask;
1032231831Sglebius}
1033231831Sglebius
103452419Sjulian/************************************************************************
103552419Sjulian			Hook routines
103652419Sjulian Names are not optional. Hooks are always connected, except for a
103770939Sjulian brief moment within these routines. On invalidation or during creation
103870939Sjulian they are connected to the 'dead' hook.
103952419Sjulian************************************************************************/
104052419Sjulian
104152419Sjulian/*
104252419Sjulian * Remove a hook reference
104352419Sjulian */
104470784Sjulianvoid
104552419Sjulianng_unref_hook(hook_p hook)
104652419Sjulian{
104771047Sjulian
1048223754Sglebius	if (hook == &ng_deadhook)
104971047Sjulian		return;
105069519Sjulian
1051223754Sglebius	if (refcount_release(&hook->hk_refs)) { /* we were the last */
1052177722Smav		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
105371047Sjulian			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
105470700Sjulian		NG_FREE_HOOK(hook);
105570700Sjulian	}
105652419Sjulian}
105752419Sjulian
105852419Sjulian/*
105952419Sjulian * Add an unconnected hook to a node. Only used internally.
106070939Sjulian * Assumes node is locked. (XXX not yet true )
106152419Sjulian */
106252419Sjulianstatic int
106352419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp)
106452419Sjulian{
106552419Sjulian	hook_p hook;
106652419Sjulian	int error = 0;
106752419Sjulian
106852419Sjulian	/* Check that the given name is good */
106952419Sjulian	if (name == NULL) {
107071047Sjulian		TRAP_ERROR();
107152419Sjulian		return (EINVAL);
107252419Sjulian	}
107354096Sarchie	if (ng_findhook(node, name) != NULL) {
107471047Sjulian		TRAP_ERROR();
107554096Sarchie		return (EEXIST);
107652419Sjulian	}
107752419Sjulian
107852419Sjulian	/* Allocate the hook and link it up */
107970784Sjulian	NG_ALLOC_HOOK(hook);
108052419Sjulian	if (hook == NULL) {
108171047Sjulian		TRAP_ERROR();
108252419Sjulian		return (ENOMEM);
108352419Sjulian	}
108470939Sjulian	hook->hk_refs = 1;		/* add a reference for us to return */
108570784Sjulian	hook->hk_flags = HK_INVALID;
108670939Sjulian	hook->hk_peer = &ng_deadhook;	/* start off this way */
108770784Sjulian	hook->hk_node = node;
108870784Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
108952419Sjulian
109070939Sjulian	/* Set hook name */
1091125028Sharti	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
109270939Sjulian
109370939Sjulian	/*
109470939Sjulian	 * Check if the node type code has something to say about it
109570939Sjulian	 * If it fails, the unref of the hook will also unref the node.
109670939Sjulian	 */
109770935Sjulian	if (node->nd_type->newhook != NULL) {
109870935Sjulian		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
109970784Sjulian			NG_HOOK_UNREF(hook);	/* this frees the hook */
110070700Sjulian			return (error);
110170700Sjulian		}
110270935Sjulian	}
110352419Sjulian	/*
110452419Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
110552419Sjulian	 * We'll ask again later when we actually connect the hooks.
110652419Sjulian	 */
110770784Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
110870784Sjulian	node->nd_numhooks++;
110970939Sjulian	NG_HOOK_REF(hook);	/* one for the node */
111052419Sjulian
111152419Sjulian	if (hookp)
111252419Sjulian		*hookp = hook;
111370939Sjulian	return (0);
111452419Sjulian}
111552419Sjulian
111652419Sjulian/*
111754096Sarchie * Find a hook
111854096Sarchie *
111954096Sarchie * Node types may supply their own optimized routines for finding
112054096Sarchie * hooks.  If none is supplied, we just do a linear search.
112170939Sjulian * XXX Possibly we should add a reference to the hook?
112254096Sarchie */
112354096Sarchiehook_p
112454096Sarchieng_findhook(node_p node, const char *name)
112554096Sarchie{
112654096Sarchie	hook_p hook;
112754096Sarchie
112870784Sjulian	if (node->nd_type->findhook != NULL)
112970784Sjulian		return (*node->nd_type->findhook)(node, name);
113070784Sjulian	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
1131229003Sglebius		if (NG_HOOK_IS_VALID(hook) &&
1132229003Sglebius		    (strcmp(NG_HOOK_NAME(hook), name) == 0))
113354096Sarchie			return (hook);
113454096Sarchie	}
113554096Sarchie	return (NULL);
113654096Sarchie}
113754096Sarchie
113854096Sarchie/*
113952419Sjulian * Destroy a hook
114052419Sjulian *
114152419Sjulian * As hooks are always attached, this really destroys two hooks.
114252419Sjulian * The one given, and the one attached to it. Disconnect the hooks
114370939Sjulian * from each other first. We reconnect the peer hook to the 'dead'
114470939Sjulian * hook so that it can still exist after we depart. We then
114570939Sjulian * send the peer its own destroy message. This ensures that we only
1146152451Sglebius * interact with the peer's structures when it is locked processing that
114770939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that
114870939Sjulian * the peer hook and node are still going to exist until
114970939Sjulian * we are finished there as the hook holds a ref on the node.
1150152451Sglebius * We run this same code again on the peer hook, but that time it is already
1151152451Sglebius * attached to the 'dead' hook.
115271047Sjulian *
1153152451Sglebius * This routine is called at all stages of hook creation
115471047Sjulian * on error detection and must be able to handle any such stage.
115552419Sjulian */
115652419Sjulianvoid
115752419Sjulianng_destroy_hook(hook_p hook)
115852419Sjulian{
1159151974Sglebius	hook_p peer;
1160151974Sglebius	node_p node;
116152419Sjulian
116271047Sjulian	if (hook == &ng_deadhook) {	/* better safe than sorry */
116371047Sjulian		printf("ng_destroy_hook called on deadhook\n");
116471047Sjulian		return;
116571047Sjulian	}
1166151974Sglebius
1167151974Sglebius	/*
1168151974Sglebius	 * Protect divorce process with mutex, to avoid races on
1169151974Sglebius	 * simultaneous disconnect.
1170151974Sglebius	 */
1171256550Smelifaro	TOPOLOGY_WLOCK();
1172151974Sglebius
1173151974Sglebius	hook->hk_flags |= HK_INVALID;
1174151974Sglebius
1175151974Sglebius	peer = NG_HOOK_PEER(hook);
1176151974Sglebius	node = NG_HOOK_NODE(hook);
1177151974Sglebius
117870939Sjulian	if (peer && (peer != &ng_deadhook)) {
117970939Sjulian		/*
118070939Sjulian		 * Set the peer to point to ng_deadhook
118170939Sjulian		 * from this moment on we are effectively independent it.
118270939Sjulian		 * send it an rmhook message of it's own.
118370939Sjulian		 */
118470939Sjulian		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
118570939Sjulian		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
118671047Sjulian		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
1187152451Sglebius			/*
118871047Sjulian			 * If it's already divorced from a node,
118971047Sjulian			 * just free it.
119071047Sjulian			 */
1191256550Smelifaro			TOPOLOGY_WUNLOCK();
119271047Sjulian		} else {
1193256550Smelifaro			TOPOLOGY_WUNLOCK();
119471047Sjulian			ng_rmhook_self(peer); 	/* Send it a surprise */
119571047Sjulian		}
119670942Sjulian		NG_HOOK_UNREF(peer);		/* account for peer link */
119770942Sjulian		NG_HOOK_UNREF(hook);		/* account for peer link */
1198151974Sglebius	} else
1199256550Smelifaro		TOPOLOGY_WUNLOCK();
120052419Sjulian
1201256550Smelifaro	TOPOLOGY_NOTOWNED();
1202151974Sglebius
120352419Sjulian	/*
120452419Sjulian	 * Remove the hook from the node's list to avoid possible recursion
120552419Sjulian	 * in case the disconnection results in node shutdown.
120652419Sjulian	 */
120771047Sjulian	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
120871047Sjulian		return;
120971047Sjulian	}
121070784Sjulian	LIST_REMOVE(hook, hk_hooks);
121170784Sjulian	node->nd_numhooks--;
121270784Sjulian	if (node->nd_type->disconnect) {
121352419Sjulian		/*
121471047Sjulian		 * The type handler may elect to destroy the node so don't
1215167677Srwatson		 * trust its existence after this point. (except
121671047Sjulian		 * that we still hold a reference on it. (which we
121771047Sjulian		 * inherrited from the hook we are destroying)
121852419Sjulian		 */
121970784Sjulian		(*node->nd_type->disconnect) (hook);
122052419Sjulian	}
122171047Sjulian
122271047Sjulian	/*
122371047Sjulian	 * Note that because we will point to ng_deadnode, the original node
122471047Sjulian	 * is not decremented automatically so we do that manually.
122571047Sjulian	 */
122671047Sjulian	_NG_HOOK_NODE(hook) = &ng_deadnode;
122771047Sjulian	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
122871047Sjulian	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
122952419Sjulian}
123052419Sjulian
123152419Sjulian/*
123252419Sjulian * Take two hooks on a node and merge the connection so that the given node
123352419Sjulian * is effectively bypassed.
123452419Sjulian */
123552419Sjulianint
123652419Sjulianng_bypass(hook_p hook1, hook_p hook2)
123752419Sjulian{
123870784Sjulian	if (hook1->hk_node != hook2->hk_node) {
123971047Sjulian		TRAP_ERROR();
124052419Sjulian		return (EINVAL);
124170784Sjulian	}
1242256550Smelifaro	TOPOLOGY_WLOCK();
1243231761Sglebius	if (NG_HOOK_NOT_VALID(hook1) || NG_HOOK_NOT_VALID(hook2)) {
1244256550Smelifaro		TOPOLOGY_WUNLOCK();
1245231761Sglebius		return (EINVAL);
1246231761Sglebius	}
124770784Sjulian	hook1->hk_peer->hk_peer = hook2->hk_peer;
124870784Sjulian	hook2->hk_peer->hk_peer = hook1->hk_peer;
124952419Sjulian
125070939Sjulian	hook1->hk_peer = &ng_deadhook;
125170939Sjulian	hook2->hk_peer = &ng_deadhook;
1252256550Smelifaro	TOPOLOGY_WUNLOCK();
125370939Sjulian
1254163244Sglebius	NG_HOOK_UNREF(hook1);
1255163244Sglebius	NG_HOOK_UNREF(hook2);
1256163244Sglebius
125752419Sjulian	/* XXX If we ever cache methods on hooks update them as well */
125852419Sjulian	ng_destroy_hook(hook1);
125952419Sjulian	ng_destroy_hook(hook2);
126052419Sjulian	return (0);
126152419Sjulian}
126252419Sjulian
126352419Sjulian/*
126452419Sjulian * Install a new netgraph type
126552419Sjulian */
126652419Sjulianint
126752419Sjulianng_newtype(struct ng_type *tp)
126852419Sjulian{
126952419Sjulian	const size_t namelen = strlen(tp->name);
127052419Sjulian
127152419Sjulian	/* Check version and type name fields */
1272229003Sglebius	if ((tp->version != NG_ABI_VERSION) || (namelen == 0) ||
1273229003Sglebius	    (namelen >= NG_TYPESIZ)) {
127471047Sjulian		TRAP_ERROR();
1275131374Sjulian		if (tp->version != NG_ABI_VERSION) {
1276229003Sglebius			printf("Netgraph: Node type rejected. ABI mismatch. "
1277229003Sglebius			    "Suggest recompile\n");
1278131374Sjulian		}
127952419Sjulian		return (EINVAL);
128052419Sjulian	}
128152419Sjulian
128252419Sjulian	/* Check for name collision */
128352419Sjulian	if (ng_findtype(tp->name) != NULL) {
128471047Sjulian		TRAP_ERROR();
128552419Sjulian		return (EEXIST);
128652419Sjulian	}
128752419Sjulian
128852419Sjulian	/* Link in new type */
1289230480Sglebius	TYPELIST_WLOCK();
129070700Sjulian	LIST_INSERT_HEAD(&ng_typelist, tp, types);
129171603Sjulian	tp->refs = 1;	/* first ref is linked list */
1292230480Sglebius	TYPELIST_WUNLOCK();
129352419Sjulian	return (0);
129452419Sjulian}
129552419Sjulian
129652419Sjulian/*
129780222Sjulian * unlink a netgraph type
129880222Sjulian * If no examples exist
129980222Sjulian */
130080222Sjulianint
130180222Sjulianng_rmtype(struct ng_type *tp)
130280222Sjulian{
130380222Sjulian	/* Check for name collision */
130480222Sjulian	if (tp->refs != 1) {
130580222Sjulian		TRAP_ERROR();
130680222Sjulian		return (EBUSY);
130780222Sjulian	}
130880222Sjulian
130980222Sjulian	/* Unlink type */
1310230480Sglebius	TYPELIST_WLOCK();
131180222Sjulian	LIST_REMOVE(tp, types);
1312230480Sglebius	TYPELIST_WUNLOCK();
131380222Sjulian	return (0);
131480222Sjulian}
131580222Sjulian
131680222Sjulian/*
131752419Sjulian * Look for a type of the name given
131852419Sjulian */
131952419Sjulianstruct ng_type *
132052419Sjulianng_findtype(const char *typename)
132152419Sjulian{
132252419Sjulian	struct ng_type *type;
132352419Sjulian
1324230480Sglebius	TYPELIST_RLOCK();
132570700Sjulian	LIST_FOREACH(type, &ng_typelist, types) {
132652419Sjulian		if (strcmp(type->name, typename) == 0)
132752419Sjulian			break;
132852419Sjulian	}
1329230480Sglebius	TYPELIST_RUNLOCK();
133052419Sjulian	return (type);
133152419Sjulian}
133252419Sjulian
133352419Sjulian/************************************************************************
133452419Sjulian			Composite routines
133552419Sjulian************************************************************************/
133652419Sjulian/*
133771047Sjulian * Connect two nodes using the specified hooks, using queued functions.
133852419Sjulian */
1339172806Smavstatic int
1340172806Smavng_con_part3(node_p node, item_p item, hook_p hook)
134152419Sjulian{
1342172806Smav	int	error = 0;
134352419Sjulian
134471047Sjulian	/*
134571047Sjulian	 * When we run, we know that the node 'node' is locked for us.
134671047Sjulian	 * Our caller has a reference on the hook.
134771047Sjulian	 * Our caller has a reference on the node.
134871047Sjulian	 * (In this case our caller is ng_apply_item() ).
134971047Sjulian	 * The peer hook has a reference on the hook.
135071849Sjulian	 * We are all set up except for the final call to the node, and
135171849Sjulian	 * the clearing of the INVALID flag.
135271047Sjulian	 */
135371047Sjulian	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
135471047Sjulian		/*
135571047Sjulian		 * The node must have been freed again since we last visited
135671047Sjulian		 * here. ng_destry_hook() has this effect but nothing else does.
135771047Sjulian		 * We should just release our references and
135871047Sjulian		 * free anything we can think of.
135971047Sjulian		 * Since we know it's been destroyed, and it's our caller
136071047Sjulian		 * that holds the references, just return.
136171047Sjulian		 */
1362172806Smav		ERROUT(ENOENT);
136352419Sjulian	}
136471047Sjulian	if (hook->hk_node->nd_type->connect) {
1365172806Smav		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
136671047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
136771849Sjulian			printf("failed in ng_con_part3()\n");
1368172806Smav			ERROUT(error);
136971047Sjulian		}
137052419Sjulian	}
137171047Sjulian	/*
137271047Sjulian	 *  XXX this is wrong for SMP. Possibly we need
137371047Sjulian	 * to separate out 'create' and 'invalid' flags.
137471047Sjulian	 * should only set flags on hooks we have locked under our node.
137571047Sjulian	 */
137671047Sjulian	hook->hk_flags &= ~HK_INVALID;
1377172806Smavdone:
1378172806Smav	NG_FREE_ITEM(item);
1379172806Smav	return (error);
138071047Sjulian}
138152419Sjulian
1382172806Smavstatic int
1383172806Smavng_con_part2(node_p node, item_p item, hook_p hook)
138471047Sjulian{
1385172806Smav	hook_p	peer;
1386172806Smav	int	error = 0;
138771047Sjulian
138852419Sjulian	/*
138971047Sjulian	 * When we run, we know that the node 'node' is locked for us.
139071047Sjulian	 * Our caller has a reference on the hook.
139171047Sjulian	 * Our caller has a reference on the node.
139271047Sjulian	 * (In this case our caller is ng_apply_item() ).
139371047Sjulian	 * The peer hook has a reference on the hook.
139471047Sjulian	 * our node pointer points to the 'dead' node.
139571047Sjulian	 * First check the hook name is unique.
139671849Sjulian	 * Should not happen because we checked before queueing this.
139752419Sjulian	 */
139871047Sjulian	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
139971047Sjulian		TRAP_ERROR();
140071047Sjulian		ng_destroy_hook(hook); /* should destroy peer too */
140171849Sjulian		printf("failed in ng_con_part2()\n");
1402172806Smav		ERROUT(EEXIST);
140371047Sjulian	}
140470939Sjulian	/*
140571047Sjulian	 * Check if the node type code has something to say about it
140671047Sjulian	 * If it fails, the unref of the hook will also unref the attached node,
140771047Sjulian	 * however since that node is 'ng_deadnode' this will do nothing.
140871047Sjulian	 * The peer hook will also be destroyed.
140970939Sjulian	 */
141071047Sjulian	if (node->nd_type->newhook != NULL) {
1411172806Smav		if ((error = (*node->nd_type->newhook)(node, hook,
1412172806Smav		    hook->hk_name))) {
141371047Sjulian			ng_destroy_hook(hook); /* should destroy peer too */
141471849Sjulian			printf("failed in ng_con_part2()\n");
1415172806Smav			ERROUT(error);
141671047Sjulian		}
141771047Sjulian	}
141871047Sjulian
141971047Sjulian	/*
142071047Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
142171047Sjulian	 * We'll ask again later when we actually connect the hooks.
142271047Sjulian	 */
142371047Sjulian	hook->hk_node = node;		/* just overwrite ng_deadnode */
142471047Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
142571047Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
142671047Sjulian	node->nd_numhooks++;
142771047Sjulian	NG_HOOK_REF(hook);	/* one for the node */
142871047Sjulian
142971047Sjulian	/*
1430167677Srwatson	 * We now have a symmetrical situation, where both hooks have been
143174078Sjulian	 * linked to their nodes, the newhook methods have been called
143271047Sjulian	 * And the references are all correct. The hooks are still marked
143371047Sjulian	 * as invalid, as we have not called the 'connect' methods
143471047Sjulian	 * yet.
1435167677Srwatson	 * We can call the local one immediately as we have the
143671047Sjulian	 * node locked, but we need to queue the remote one.
143771047Sjulian	 */
143871047Sjulian	if (hook->hk_node->nd_type->connect) {
1439172806Smav		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
144071047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
144171849Sjulian			printf("failed in ng_con_part2(A)\n");
1442172806Smav			ERROUT(error);
144371047Sjulian		}
144471047Sjulian	}
1445151974Sglebius
1446151974Sglebius	/*
1447151974Sglebius	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1448151974Sglebius	 */
1449256550Smelifaro	TOPOLOGY_RLOCK();
1450151974Sglebius	peer = hook->hk_peer;
1451151974Sglebius	if (peer == &ng_deadhook) {
1452256550Smelifaro		TOPOLOGY_RUNLOCK();
1453151974Sglebius		printf("failed in ng_con_part2(B)\n");
1454151974Sglebius		ng_destroy_hook(hook);
1455172806Smav		ERROUT(ENOENT);
1456151974Sglebius	}
1457256550Smelifaro	TOPOLOGY_RUNLOCK();
1458151974Sglebius
1459173605Sglebius	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1460173605Sglebius	    NULL, 0, NG_REUSE_ITEM))) {
1461151974Sglebius		printf("failed in ng_con_part2(C)\n");
146271849Sjulian		ng_destroy_hook(hook);	/* also zaps peer */
1463172806Smav		return (error);		/* item was consumed. */
146471849Sjulian	}
146571047Sjulian	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1466172806Smav	return (0);			/* item was consumed. */
1467172806Smavdone:
1468172806Smav	NG_FREE_ITEM(item);
1469172806Smav	return (error);
147052419Sjulian}
147152419Sjulian
147252419Sjulian/*
1473152451Sglebius * Connect this node with another node. We assume that this node is
147471047Sjulian * currently locked, as we are only called from an NGM_CONNECT message.
147552419Sjulian */
147671047Sjulianstatic int
1477172806Smavng_con_nodes(item_p item, node_p node, const char *name,
1478172806Smav    node_p node2, const char *name2)
147952419Sjulian{
1480152451Sglebius	int	error;
1481152451Sglebius	hook_p	hook;
1482152451Sglebius	hook_p	hook2;
148352419Sjulian
148471849Sjulian	if (ng_findhook(node2, name2) != NULL) {
148571849Sjulian		return(EEXIST);
148671849Sjulian	}
148770939Sjulian	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
148852419Sjulian		return (error);
148971047Sjulian	/* Allocate the other hook and link it up */
149071047Sjulian	NG_ALLOC_HOOK(hook2);
1491140737Sglebius	if (hook2 == NULL) {
149271047Sjulian		TRAP_ERROR();
149371047Sjulian		ng_destroy_hook(hook);	/* XXX check ref counts so far */
149471047Sjulian		NG_HOOK_UNREF(hook);	/* including our ref */
149571047Sjulian		return (ENOMEM);
149671047Sjulian	}
149771047Sjulian	hook2->hk_refs = 1;		/* start with a reference for us. */
149871047Sjulian	hook2->hk_flags = HK_INVALID;
149971047Sjulian	hook2->hk_peer = hook;		/* Link the two together */
150071047Sjulian	hook->hk_peer = hook2;
150171047Sjulian	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
150271047Sjulian	NG_HOOK_REF(hook2);
1503152451Sglebius	hook2->hk_node = &ng_deadnode;
1504125028Sharti	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
150571047Sjulian
150671047Sjulian	/*
150771047Sjulian	 * Queue the function above.
150871047Sjulian	 * Procesing continues in that function in the lock context of
150971047Sjulian	 * the other node.
151071047Sjulian	 */
1511173605Sglebius	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1512173605Sglebius	    NG_NOFLAGS))) {
1513171885Smav		printf("failed in ng_con_nodes(): %d\n", error);
1514171885Smav		ng_destroy_hook(hook);	/* also zaps peer */
1515171885Smav	}
151671047Sjulian
151771047Sjulian	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
151871047Sjulian	NG_HOOK_UNREF(hook2);
1519171885Smav	return (error);
152071047Sjulian}
152171047Sjulian
152271047Sjulian/*
152371047Sjulian * Make a peer and connect.
152471047Sjulian * We assume that the local node is locked.
152571047Sjulian * The new node probably doesn't need a lock until
152671047Sjulian * it has a hook, because it cannot really have any work until then,
152771047Sjulian * but we should think about it a bit more.
152871047Sjulian *
152971047Sjulian * The problem may come if the other node also fires up
153071047Sjulian * some hardware or a timer or some other source of activation,
153171047Sjulian * also it may already get a command msg via it's ID.
153271047Sjulian *
153371047Sjulian * We could use the same method as ng_con_nodes() but we'd have
1534152451Sglebius * to add ability to remove the node when failing. (Not hard, just
153571047Sjulian * make arg1 point to the node to remove).
153671047Sjulian * Unless of course we just ignore failure to connect and leave
153771047Sjulian * an unconnected node?
153871047Sjulian */
153971047Sjulianstatic int
154071047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type)
154171047Sjulian{
1542152451Sglebius	node_p	node2;
1543152451Sglebius	hook_p	hook1, hook2;
1544152451Sglebius	int	error;
154571047Sjulian
154671047Sjulian	if ((error = ng_make_node(type, &node2))) {
154752419Sjulian		return (error);
154852419Sjulian	}
154970939Sjulian
155071047Sjulian	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
155171849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
155271047Sjulian		return (error);
155371047Sjulian	}
155471047Sjulian
155571047Sjulian	if ((error = ng_add_hook(node2, name2, &hook2))) {
155671849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
155771047Sjulian		ng_destroy_hook(hook1);
155871047Sjulian		NG_HOOK_UNREF(hook1);
155971047Sjulian		return (error);
156071047Sjulian	}
156171047Sjulian
156270939Sjulian	/*
156371047Sjulian	 * Actually link the two hooks together.
156471047Sjulian	 */
156571047Sjulian	hook1->hk_peer = hook2;
156671047Sjulian	hook2->hk_peer = hook1;
156771047Sjulian
156871047Sjulian	/* Each hook is referenced by the other */
156971047Sjulian	NG_HOOK_REF(hook1);
157071047Sjulian	NG_HOOK_REF(hook2);
157171047Sjulian
157271047Sjulian	/* Give each node the opportunity to veto the pending connection */
157371047Sjulian	if (hook1->hk_node->nd_type->connect) {
157471047Sjulian		error = (*hook1->hk_node->nd_type->connect) (hook1);
157571047Sjulian	}
157671047Sjulian
157771047Sjulian	if ((error == 0) && hook2->hk_node->nd_type->connect) {
157871047Sjulian		error = (*hook2->hk_node->nd_type->connect) (hook2);
157971047Sjulian
158071047Sjulian	}
158171047Sjulian
158271047Sjulian	/*
158370939Sjulian	 * drop the references we were holding on the two hooks.
158470939Sjulian	 */
158571047Sjulian	if (error) {
158671047Sjulian		ng_destroy_hook(hook2);	/* also zaps hook1 */
158771849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
158871047Sjulian	} else {
158971047Sjulian		/* As a last act, allow the hooks to be used */
159071047Sjulian		hook1->hk_flags &= ~HK_INVALID;
159171047Sjulian		hook2->hk_flags &= ~HK_INVALID;
159271047Sjulian	}
159371047Sjulian	NG_HOOK_UNREF(hook1);
159470939Sjulian	NG_HOOK_UNREF(hook2);
159570939Sjulian	return (error);
159652419Sjulian}
159771047Sjulian
159870700Sjulian/************************************************************************
159970700Sjulian		Utility routines to send self messages
160070700Sjulian************************************************************************/
160170700Sjulian
160271849Sjulian/* Shut this node down as soon as everyone is clear of it */
1603167677Srwatson/* Should add arg "immediately" to jump the queue */
160470700Sjulianint
1605186060Smavng_rmnode_self(node_p node)
160670700Sjulian{
160771849Sjulian	int		error;
160852419Sjulian
160971849Sjulian	if (node == &ng_deadnode)
161071849Sjulian		return (0);
1611132464Sjulian	node->nd_flags |= NGF_INVALID;
1612132464Sjulian	if (node->nd_flags & NGF_CLOSING)
161371849Sjulian		return (0);
161470700Sjulian
1615186060Smav	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
161671849Sjulian	return (error);
161770700Sjulian}
161870700Sjulian
161971849Sjulianstatic void
162071047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
162171047Sjulian{
162271047Sjulian	ng_destroy_hook(hook);
162371849Sjulian	return ;
162471047Sjulian}
162571047Sjulian
162670935Sjulianint
162770935Sjulianng_rmhook_self(hook_p hook)
162870935Sjulian{
162971047Sjulian	int		error;
163070935Sjulian	node_p node = NG_HOOK_NODE(hook);
163170935Sjulian
163271047Sjulian	if (node == &ng_deadnode)
163371047Sjulian		return (0);
163471047Sjulian
163571047Sjulian	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
163671047Sjulian	return (error);
163770935Sjulian}
163870935Sjulian
163970700Sjulian/***********************************************************************
164052419Sjulian * Parse and verify a string of the form:  <NODE:><PATH>
164152419Sjulian *
164252419Sjulian * Such a string can refer to a specific node or a specific hook
164352419Sjulian * on a specific node, depending on how you look at it. In the
164452419Sjulian * latter case, the PATH component must not end in a dot.
164552419Sjulian *
164652419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string
164752419Sjulian * of hook names separated by dots. This breaks out the original
164852419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp
164952419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to
165052419Sjulian * the final hook component of <PATH>, if any, otherwise NULL.
165152419Sjulian *
165252419Sjulian * This returns -1 if the path is malformed. The char ** are optional.
165370700Sjulian ***********************************************************************/
165452419Sjulianint
165552419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
165652419Sjulian{
1657152451Sglebius	char	*node, *path, *hook;
1658152451Sglebius	int	k;
165952419Sjulian
166052419Sjulian	/*
166152419Sjulian	 * Extract absolute NODE, if any
166252419Sjulian	 */
166352419Sjulian	for (path = addr; *path && *path != ':'; path++);
166452419Sjulian	if (*path) {
166552419Sjulian		node = addr;	/* Here's the NODE */
166652419Sjulian		*path++ = '\0';	/* Here's the PATH */
166752419Sjulian
166852419Sjulian		/* Node name must not be empty */
166952419Sjulian		if (!*node)
167052419Sjulian			return -1;
167152419Sjulian
167252419Sjulian		/* A name of "." is OK; otherwise '.' not allowed */
167352419Sjulian		if (strcmp(node, ".") != 0) {
167452419Sjulian			for (k = 0; node[k]; k++)
167552419Sjulian				if (node[k] == '.')
167652419Sjulian					return -1;
167752419Sjulian		}
167852419Sjulian	} else {
167952419Sjulian		node = NULL;	/* No absolute NODE */
168052419Sjulian		path = addr;	/* Here's the PATH */
168152419Sjulian	}
168252419Sjulian
168352419Sjulian	/* Snoop for illegal characters in PATH */
168452419Sjulian	for (k = 0; path[k]; k++)
168552419Sjulian		if (path[k] == ':')
168652419Sjulian			return -1;
168752419Sjulian
168852419Sjulian	/* Check for no repeated dots in PATH */
168952419Sjulian	for (k = 0; path[k]; k++)
169052419Sjulian		if (path[k] == '.' && path[k + 1] == '.')
169152419Sjulian			return -1;
169252419Sjulian
169352419Sjulian	/* Remove extra (degenerate) dots from beginning or end of PATH */
169452419Sjulian	if (path[0] == '.')
169552419Sjulian		path++;
169652419Sjulian	if (*path && path[strlen(path) - 1] == '.')
169752419Sjulian		path[strlen(path) - 1] = 0;
169852419Sjulian
169952419Sjulian	/* If PATH has a dot, then we're not talking about a hook */
170052419Sjulian	if (*path) {
170152419Sjulian		for (hook = path, k = 0; path[k]; k++)
170252419Sjulian			if (path[k] == '.') {
170352419Sjulian				hook = NULL;
170452419Sjulian				break;
170552419Sjulian			}
170652419Sjulian	} else
170752419Sjulian		path = hook = NULL;
170852419Sjulian
170952419Sjulian	/* Done */
171052419Sjulian	if (nodep)
171152419Sjulian		*nodep = node;
171252419Sjulian	if (pathp)
171352419Sjulian		*pathp = path;
171452419Sjulian	if (hookp)
171552419Sjulian		*hookp = hook;
171652419Sjulian	return (0);
171752419Sjulian}
171852419Sjulian
171952419Sjulian/*
172052419Sjulian * Given a path, which may be absolute or relative, and a starting node,
172170700Sjulian * return the destination node.
172252419Sjulian */
172352419Sjulianint
1724229003Sglebiusng_path2noderef(node_p here, const char *address, node_p *destp,
1725229003Sglebius    hook_p *lasthook)
172652419Sjulian{
1727125028Sharti	char    fullpath[NG_PATHSIZ];
1728219827Sglebius	char   *nodename, *path;
172970700Sjulian	node_p  node, oldnode;
173052419Sjulian
173152419Sjulian	/* Initialize */
173270784Sjulian	if (destp == NULL) {
173371047Sjulian		TRAP_ERROR();
173452419Sjulian		return EINVAL;
173570784Sjulian	}
173652419Sjulian	*destp = NULL;
173752419Sjulian
173852419Sjulian	/* Make a writable copy of address for ng_path_parse() */
173952419Sjulian	strncpy(fullpath, address, sizeof(fullpath) - 1);
174052419Sjulian	fullpath[sizeof(fullpath) - 1] = '\0';
174152419Sjulian
174252419Sjulian	/* Parse out node and sequence of hooks */
174352419Sjulian	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
174471047Sjulian		TRAP_ERROR();
174552419Sjulian		return EINVAL;
174652419Sjulian	}
174752419Sjulian
174870700Sjulian	/*
174970700Sjulian	 * For an absolute address, jump to the starting node.
175070700Sjulian	 * Note that this holds a reference on the node for us.
175170700Sjulian	 * Don't forget to drop the reference if we don't need it.
175270700Sjulian	 */
175352419Sjulian	if (nodename) {
175470700Sjulian		node = ng_name2noderef(here, nodename);
175552419Sjulian		if (node == NULL) {
175671047Sjulian			TRAP_ERROR();
175752419Sjulian			return (ENOENT);
175852419Sjulian		}
175970700Sjulian	} else {
176070700Sjulian		if (here == NULL) {
176171047Sjulian			TRAP_ERROR();
176270700Sjulian			return (EINVAL);
176370700Sjulian		}
176452419Sjulian		node = here;
176570784Sjulian		NG_NODE_REF(node);
176670700Sjulian	}
176752419Sjulian
1768219827Sglebius	if (path == NULL) {
1769219827Sglebius		if (lasthook != NULL)
1770219827Sglebius			*lasthook = NULL;
1771219827Sglebius		*destp = node;
1772219827Sglebius		return (0);
1773219827Sglebius	}
1774219827Sglebius
177570700Sjulian	/*
1776152451Sglebius	 * Now follow the sequence of hooks
1777219827Sglebius	 *
1778219827Sglebius	 * XXXGL: The path may demolish as we go the sequence, but if
1779219827Sglebius	 * we hold the topology mutex at critical places, then, I hope,
1780219827Sglebius	 * we would always have valid pointers in hand, although the
1781219827Sglebius	 * path behind us may no longer exist.
178270700Sjulian	 */
1783219827Sglebius	for (;;) {
1784219827Sglebius		hook_p hook;
178552419Sjulian		char *segment;
178652419Sjulian
178752419Sjulian		/*
178852419Sjulian		 * Break out the next path segment. Replace the dot we just
1789219827Sglebius		 * found with a NUL; "path" points to the next segment (or the
179052419Sjulian		 * NUL at the end).
179152419Sjulian		 */
1792219827Sglebius		for (segment = path; *path != '\0'; path++) {
1793219827Sglebius			if (*path == '.') {
1794219827Sglebius				*path++ = '\0';
179552419Sjulian				break;
179652419Sjulian			}
179752419Sjulian		}
179852419Sjulian
179952419Sjulian		/* We have a segment, so look for a hook by that name */
180054096Sarchie		hook = ng_findhook(node, segment);
180152419Sjulian
1802256550Smelifaro		TOPOLOGY_WLOCK();
180352419Sjulian		/* Can't get there from here... */
1804229003Sglebius		if (hook == NULL || NG_HOOK_PEER(hook) == NULL ||
1805229003Sglebius		    NG_HOOK_NOT_VALID(hook) ||
1806229003Sglebius		    NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
180771047Sjulian			TRAP_ERROR();
180870784Sjulian			NG_NODE_UNREF(node);
1809256550Smelifaro			TOPOLOGY_WUNLOCK();
181052419Sjulian			return (ENOENT);
181152419Sjulian		}
181252419Sjulian
181370700Sjulian		/*
1814152451Sglebius		 * Hop on over to the next node
181570700Sjulian		 * XXX
1816152451Sglebius		 * Big race conditions here as hooks and nodes go away
181770700Sjulian		 * *** Idea.. store an ng_ID_t in each hook and use that
181870700Sjulian		 * instead of the direct hook in this crawl?
181970700Sjulian		 */
182070700Sjulian		oldnode = node;
182170784Sjulian		if ((node = NG_PEER_NODE(hook)))
182270784Sjulian			NG_NODE_REF(node);	/* XXX RACE */
182370784Sjulian		NG_NODE_UNREF(oldnode);	/* XXX another race */
182470784Sjulian		if (NG_NODE_NOT_VALID(node)) {
182570784Sjulian			NG_NODE_UNREF(node);	/* XXX more races */
1826256550Smelifaro			TOPOLOGY_WUNLOCK();
1827219827Sglebius			TRAP_ERROR();
1828219827Sglebius			return (ENXIO);
182970700Sjulian		}
183052419Sjulian
1831219827Sglebius		if (*path == '\0') {
1832219827Sglebius			if (lasthook != NULL) {
1833219827Sglebius				if (hook != NULL) {
1834219827Sglebius					*lasthook = NG_HOOK_PEER(hook);
1835219827Sglebius					NG_HOOK_REF(*lasthook);
1836219827Sglebius				} else
1837219827Sglebius					*lasthook = NULL;
1838219827Sglebius			}
1839256550Smelifaro			TOPOLOGY_WUNLOCK();
1840219827Sglebius			*destp = node;
1841219827Sglebius			return (0);
1842219827Sglebius		}
1843256550Smelifaro		TOPOLOGY_WUNLOCK();
184452419Sjulian	}
184552419Sjulian}
184652419Sjulian
184770700Sjulian/***************************************************************\
184870700Sjulian* Input queue handling.
184970700Sjulian* All activities are submitted to the node via the input queue
185070700Sjulian* which implements a multiple-reader/single-writer gate.
1851167677Srwatson* Items which cannot be handled immediately are queued.
185270700Sjulian*
185370700Sjulian* read-write queue locking inline functions			*
185470700Sjulian\***************************************************************/
185570700Sjulian
1856178228Smavstatic __inline void	ng_queue_rw(node_p node, item_p  item, int rw);
1857178228Smavstatic __inline item_p	ng_dequeue(node_p node, int *rw);
1858178228Smavstatic __inline item_p	ng_acquire_read(node_p node, item_p  item);
1859178228Smavstatic __inline item_p	ng_acquire_write(node_p node, item_p  item);
1860178228Smavstatic __inline void	ng_leave_read(node_p node);
1861178228Smavstatic __inline void	ng_leave_write(node_p node);
186270700Sjulian
186352419Sjulian/*
186470700Sjulian * Definition of the bits fields in the ng_queue flag word.
186570700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle
186670700Sjulian * with them.
186770700Sjulian *
186871902Sjulian * The ordering here may be important! don't shuffle these.
186952419Sjulian */
187070700Sjulian/*-
187170700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet)
187270700Sjulian                       |
187370700Sjulian                       V
187470700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
1875151973Sglebius  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
1876151973Sglebius  | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
1877151973Sglebius  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
187870700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
1879151973Sglebius  \___________________________ ____________________________/ | |
1880151973Sglebius                            V                                | |
1881151973Sglebius                  [active reader count]                      | |
188270700Sjulian                                                             | |
1883151973Sglebius            Operation Pending -------------------------------+ |
188470700Sjulian                                                               |
1885151973Sglebius          Active Writer ---------------------------------------+
188671902Sjulian
1887178039SmavNode queue has such semantics:
1888178039Smav- All flags modifications are atomic.
1889178039Smav- Reader count can be incremented only if there is no writer or pending flags.
1890178039Smav  As soon as this can't be done with single operation, it is implemented with
1891178039Smav  spin loop and atomic_cmpset().
1892178039Smav- Writer flag can be set only if there is no any bits set.
1893178039Smav  It is implemented with atomic_cmpset().
1894178039Smav- Pending flag can be set any time, but to avoid collision on queue processing
1895178039Smav  all queue fields are protected by the mutex.
1896178039Smav- Queue processing thread reads queue holding the mutex, but releases it while
1897178039Smav  processing. When queue is empty pending flag is removed.
1898178039Smav*/
189971902Sjulian
1900151973Sglebius#define WRITER_ACTIVE	0x00000001
1901151973Sglebius#define OP_PENDING	0x00000002
1902151973Sglebius#define READER_INCREMENT 0x00000004
1903151973Sglebius#define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
1904151973Sglebius#define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
190571902Sjulian
190671902Sjulian/* Defines of more elaborate states on the queue */
1907151973Sglebius/* Mask of bits a new read cares about */
1908151973Sglebius#define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
190971902Sjulian
1910151973Sglebius/* Mask of bits a new write cares about */
191171902Sjulian#define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
191271902Sjulian
1913151973Sglebius/* Test to decide if there is something on the queue. */
1914151973Sglebius#define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
191571902Sjulian
1916151973Sglebius/* How to decide what the next queued item is. */
1917178228Smav#define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
1918178228Smav#define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
1919151973Sglebius
1920151973Sglebius/* Read the status to decide if the next item on the queue can now run. */
1921151973Sglebius#define QUEUED_READER_CAN_PROCEED(QP)			\
1922151973Sglebius		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
1923151973Sglebius#define QUEUED_WRITER_CAN_PROCEED(QP)			\
1924151973Sglebius		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1925151973Sglebius
192671902Sjulian/* Is there a chance of getting ANY work off the queue? */
1927151973Sglebius#define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
1928151973Sglebius	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1929177953Smav				QUEUED_WRITER_CAN_PROCEED(QP))
193071902Sjulian
1931151238Sglebius#define NGQRW_R 0
1932151238Sglebius#define NGQRW_W 1
1933151238Sglebius
1934178228Smav#define NGQ2_WORKQ	0x00000001
1935178228Smav
193670700Sjulian/*
193770700Sjulian * Taking into account the current state of the queue and node, possibly take
193870700Sjulian * the next entry off the queue and return it. Return NULL if there was
193970700Sjulian * nothing we could return, either because there really was nothing there, or
194070700Sjulian * because the node was in a state where it cannot yet process the next item
194170700Sjulian * on the queue.
194270700Sjulian */
194370700Sjulianstatic __inline item_p
1944178228Smavng_dequeue(node_p node, int *rw)
194570700Sjulian{
194670700Sjulian	item_p item;
1947178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
194871902Sjulian
1949178039Smav	/* This MUST be called with the mutex held. */
1950139039Sglebius	mtx_assert(&ngq->q_mtx, MA_OWNED);
1951178039Smav
1952178039Smav	/* If there is nothing queued, then just return. */
1953151973Sglebius	if (!QUEUE_ACTIVE(ngq)) {
1954154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
1955154275Sglebius		    "queue flags 0x%lx", __func__,
1956178228Smav		    node->nd_ID, node, ngq->q_flags);
1957151973Sglebius		return (NULL);
1958151973Sglebius	}
1959139039Sglebius
1960151973Sglebius	/*
1961151973Sglebius	 * From here, we can assume there is a head item.
1962151973Sglebius	 * We need to find out what it is and if it can be dequeued, given
1963151973Sglebius	 * the current state of the node.
1964151973Sglebius	 */
1965151973Sglebius	if (HEAD_IS_READER(ngq)) {
1966177953Smav		while (1) {
1967177953Smav			long t = ngq->q_flags;
1968177953Smav			if (t & WRITER_ACTIVE) {
1969178039Smav				/* There is writer, reader can't proceed. */
1970229003Sglebius				CTR4(KTR_NET, "%20s: node [%x] (%p) queued "
1971229003Sglebius				    "reader can't proceed; queue flags 0x%lx",
1972229003Sglebius				    __func__, node->nd_ID, node, t);
1973177953Smav				return (NULL);
1974177953Smav			}
1975178228Smav			if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1976177953Smav			    t + READER_INCREMENT))
1977177953Smav				break;
1978177953Smav			cpu_spinwait();
1979151973Sglebius		}
1980178039Smav		/* We have got reader lock for the node. */
1981151238Sglebius		*rw = NGQRW_R;
1982178228Smav	} else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1983177953Smav	    OP_PENDING + WRITER_ACTIVE)) {
1984178039Smav		/* We have got writer lock for the node. */
1985151238Sglebius		*rw = NGQRW_W;
198670700Sjulian	} else {
1987178039Smav		/* There is somebody other, writer can't proceed. */
1988229003Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't "
1989229003Sglebius		    "proceed; queue flags 0x%lx", __func__, node->nd_ID, node,
1990229003Sglebius		    ngq->q_flags);
1991151973Sglebius		return (NULL);
199270700Sjulian	}
199352419Sjulian
199470700Sjulian	/*
199570700Sjulian	 * Now we dequeue the request (whatever it may be) and correct the
199670700Sjulian	 * pending flags and the next and last pointers.
199770700Sjulian	 */
1998178228Smav	item = STAILQ_FIRST(&ngq->queue);
1999178228Smav	STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
2000178228Smav	if (STAILQ_EMPTY(&ngq->queue))
2001178228Smav		atomic_clear_int(&ngq->q_flags, OP_PENDING);
2002229003Sglebius	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue "
2003229003Sglebius	    "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" :
2004229003Sglebius	    "READER", ngq->q_flags);
200570700Sjulian	return (item);
200670700Sjulian}
200752419Sjulian
200870700Sjulian/*
2009178039Smav * Queue a packet to be picked up later by someone else.
2010178039Smav * If the queue could be run now, add node to the queue handler's worklist.
201170700Sjulian */
201270700Sjulianstatic __inline void
2013178228Smavng_queue_rw(node_p node, item_p  item, int rw)
201470700Sjulian{
2015178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
2016151973Sglebius	if (rw == NGQRW_W)
2017151973Sglebius		NGI_SET_WRITER(item);
2018151973Sglebius	else
2019151973Sglebius		NGI_SET_READER(item);
2020241009Srstone	item->depth = 1;
2021177953Smav
2022177953Smav	NG_QUEUE_LOCK(ngq);
2023177953Smav	/* Set OP_PENDING flag and enqueue the item. */
2024178228Smav	atomic_set_int(&ngq->q_flags, OP_PENDING);
2025178228Smav	STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
2026177953Smav
2027154275Sglebius	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
2028178228Smav	    node->nd_ID, node, item, rw ? "WRITER" : "READER" );
2029229003Sglebius
203070700Sjulian	/*
2031151973Sglebius	 * We can take the worklist lock with the node locked
2032151973Sglebius	 * BUT NOT THE REVERSE!
2033151973Sglebius	 */
2034151973Sglebius	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2035178228Smav		ng_worklist_add(node);
2036177953Smav	NG_QUEUE_UNLOCK(ngq);
203770700Sjulian}
203852419Sjulian
2039178039Smav/* Acquire reader lock on node. If node is busy, queue the packet. */
204070700Sjulianstatic __inline item_p
2041178228Smavng_acquire_read(node_p node, item_p item)
204252419Sjulian{
2043178228Smav	KASSERT(node != &ng_deadnode,
2044151974Sglebius	    ("%s: working on deadnode", __func__));
204552419Sjulian
2046177953Smav	/* Reader needs node without writer and pending items. */
2047229003Sglebius	for (;;) {
2048178228Smav		long t = node->nd_input_queue.q_flags;
2049177953Smav		if (t & NGQ_RMASK)
2050177953Smav			break; /* Node is not ready for reader. */
2051229003Sglebius		if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t,
2052229003Sglebius		    t + READER_INCREMENT)) {
2053177953Smav	    		/* Successfully grabbed node */
2054177953Smav			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
2055178228Smav			    __func__, node->nd_ID, node, item);
2056177953Smav			return (item);
2057177953Smav		}
2058177953Smav		cpu_spinwait();
2059297793Spfg	}
206070700Sjulian
2061177953Smav	/* Queue the request for later. */
2062178228Smav	ng_queue_rw(node, item, NGQRW_R);
206370700Sjulian
2064148236Sglebius	return (NULL);
206570700Sjulian}
206670700Sjulian
2067178039Smav/* Acquire writer lock on node. If node is busy, queue the packet. */
206870700Sjulianstatic __inline item_p
2069178228Smavng_acquire_write(node_p node, item_p item)
207070700Sjulian{
2071178228Smav	KASSERT(node != &ng_deadnode,
2072151974Sglebius	    ("%s: working on deadnode", __func__));
2073151974Sglebius
2074177953Smav	/* Writer needs completely idle node. */
2075229003Sglebius	if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0,
2076229003Sglebius	    WRITER_ACTIVE)) {
2077177953Smav	    	/* Successfully grabbed node */
2078154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
2079178228Smav		    __func__, node->nd_ID, node, item);
208070700Sjulian		return (item);
208152419Sjulian	}
208252419Sjulian
2083177953Smav	/* Queue the request for later. */
2084178228Smav	ng_queue_rw(node, item, NGQRW_W);
208570700Sjulian
2086148236Sglebius	return (NULL);
208770700Sjulian}
208870700Sjulian
2089167385Sjulian#if 0
2090167385Sjulianstatic __inline item_p
2091178228Smavng_upgrade_write(node_p node, item_p item)
2092167385Sjulian{
2093178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
2094178228Smav	KASSERT(node != &ng_deadnode,
2095167385Sjulian	    ("%s: working on deadnode", __func__));
2096167385Sjulian
2097167385Sjulian	NGI_SET_WRITER(item);
2098167385Sjulian
2099178228Smav	NG_QUEUE_LOCK(ngq);
2100167385Sjulian
2101167385Sjulian	/*
2102167385Sjulian	 * There will never be no readers as we are there ourselves.
2103167385Sjulian	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2104167385Sjulian	 * The caller we are running from will call ng_leave_read()
2105167385Sjulian	 * soon, so we must account for that. We must leave again with the
2106167385Sjulian	 * READER lock. If we find other readers, then
2107167385Sjulian	 * queue the request for later. However "later" may be rignt now
2108167385Sjulian	 * if there are no readers. We don't really care if there are queued
2109167385Sjulian	 * items as we will bypass them anyhow.
2110167385Sjulian	 */
2111178228Smav	atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
2112178228Smav	if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
2113178228Smav		NG_QUEUE_UNLOCK(ngq);
2114167385Sjulian
2115167385Sjulian		/* It's just us, act on the item. */
2116167385Sjulian		/* will NOT drop writer lock when done */
2117167385Sjulian		ng_apply_item(node, item, 0);
2118167385Sjulian
2119167385Sjulian		/*
2120229003Sglebius		 * Having acted on the item, atomically
2121229003Sglebius		 * downgrade back to READER and finish up.
2122167385Sjulian	 	 */
2123229003Sglebius		atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2124167385Sjulian
2125167385Sjulian		/* Our caller will call ng_leave_read() */
2126167385Sjulian		return;
2127167385Sjulian	}
2128167385Sjulian	/*
2129167385Sjulian	 * It's not just us active, so queue us AT THE HEAD.
2130167385Sjulian	 * "Why?" I hear you ask.
2131167385Sjulian	 * Put us at the head of the queue as we've already been
2132167385Sjulian	 * through it once. If there is nothing else waiting,
2133167385Sjulian	 * set the correct flags.
2134167385Sjulian	 */
2135178228Smav	if (STAILQ_EMPTY(&ngq->queue)) {
2136167385Sjulian		/* We've gone from, 0 to 1 item in the queue */
2137178228Smav		atomic_set_int(&ngq->q_flags, OP_PENDING);
2138167385Sjulian
2139167385Sjulian		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
2140178228Smav		    node->nd_ID, node);
2141167385Sjulian	};
2142178228Smav	STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
2143178228Smav	CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
2144178228Smav	    __func__, node->nd_ID, node, item );
2145167385Sjulian
2146167385Sjulian	/* Reverse what we did above. That downgrades us back to reader */
2147178228Smav	atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2148177953Smav	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2149178228Smav		ng_worklist_add(node);
2150178228Smav	NG_QUEUE_UNLOCK(ngq);
2151167385Sjulian
2152167385Sjulian	return;
2153167385Sjulian}
2154167385Sjulian#endif
2155167385Sjulian
2156178039Smav/* Release reader lock. */
215770700Sjulianstatic __inline void
2158178228Smavng_leave_read(node_p node)
215970700Sjulian{
2160178228Smav	atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
216170700Sjulian}
216270700Sjulian
2163178039Smav/* Release writer lock. */
216470700Sjulianstatic __inline void
2165178228Smavng_leave_write(node_p node)
216670700Sjulian{
2167178228Smav	atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
216870700Sjulian}
216970700Sjulian
2170178039Smav/* Purge node queue. Called on node shutdown. */
217170700Sjulianstatic void
2172178228Smavng_flush_input_queue(node_p node)
217370700Sjulian{
2174178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
217570700Sjulian	item_p item;
2176151973Sglebius
2177168049Swkoszek	NG_QUEUE_LOCK(ngq);
2178178228Smav	while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
2179178228Smav		STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
2180178228Smav		if (STAILQ_EMPTY(&ngq->queue))
2181178228Smav			atomic_clear_int(&ngq->q_flags, OP_PENDING);
2182168049Swkoszek		NG_QUEUE_UNLOCK(ngq);
218370700Sjulian
2184151973Sglebius		/* If the item is supplying a callback, call it with an error */
2185177071Smav		if (item->apply != NULL) {
2186177071Smav			if (item->depth == 1)
2187177071Smav				item->apply->error = ENOENT;
2188177071Smav			if (refcount_release(&item->apply->refs)) {
2189177071Smav				(*item->apply->apply)(item->apply->context,
2190177071Smav				    item->apply->error);
2191177071Smav			}
2192151283Sglebius		}
2193132369Sjulian		NG_FREE_ITEM(item);
2194168049Swkoszek		NG_QUEUE_LOCK(ngq);
219570700Sjulian	}
2196168049Swkoszek	NG_QUEUE_UNLOCK(ngq);
219770700Sjulian}
219870700Sjulian
219970700Sjulian/***********************************************************************
220070700Sjulian* Externally visible method for sending or queueing messages or data.
220170700Sjulian***********************************************************************/
220270700Sjulian
220370700Sjulian/*
220471849Sjulian * The module code should have filled out the item correctly by this stage:
220570700Sjulian * Common:
220670700Sjulian *    reference to destination node.
220770700Sjulian *    Reference to destination rcv hook if relevant.
2208172806Smav *    apply pointer must be or NULL or reference valid struct ng_apply_info.
220970700Sjulian * Data:
221070700Sjulian *    pointer to mbuf
221170700Sjulian * Control_Message:
221270700Sjulian *    pointer to msg.
221370700Sjulian *    ID of original sender node. (return address)
221471849Sjulian * Function:
221571849Sjulian *    Function pointer
221671849Sjulian *    void * argument
221771849Sjulian *    integer argument
221870700Sjulian *
221970700Sjulian * The nodes have several routines and macros to help with this task:
222070700Sjulian */
222170700Sjulian
222270700Sjulianint
2223146281Sglebiusng_snd_item(item_p item, int flags)
222470700Sjulian{
2225172806Smav	hook_p hook;
2226172806Smav	node_p node;
2227146281Sglebius	int queue, rw;
2228172806Smav	struct ng_queue *ngq;
2229151256Sglebius	int error = 0;
223070700Sjulian
2231176046Smav	/* We are sending item, so it must be present! */
2232176046Smav	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2233172806Smav
223470784Sjulian#ifdef	NETGRAPH_DEBUG
2235152451Sglebius	_ngi_check(item, __FILE__, __LINE__);
223670700Sjulian#endif
223770700Sjulian
2238176046Smav	/* Item was sent once more, postpone apply() call. */
2239172806Smav	if (item->apply)
2240172806Smav		refcount_acquire(&item->apply->refs);
2241146281Sglebius
2242172806Smav	node = NGI_NODE(item);
2243176046Smav	/* Node is never optional. */
2244176046Smav	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2245172806Smav
2246175850Smav	hook = NGI_HOOK(item);
2247176046Smav	/* Valid hook and mbuf are mandatory for data. */
2248176046Smav	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2249176046Smav		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
2250131123Sjulian		if (NGI_M(item) == NULL)
2251172806Smav			ERROUT(EINVAL);
225270700Sjulian		CHECK_DATA_MBUF(NGI_M(item));
225369922Sjulian	}
2254149505Sglebius
225570700Sjulian	/*
2256176046Smav	 * If the item or the node specifies single threading, force
2257176046Smav	 * writer semantics. Similarly, the node may say one hook always
2258176046Smav	 * produces writers. These are overrides.
225970700Sjulian	 */
2260176567Smav	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2261176046Smav	    (node->nd_flags & NGF_FORCE_WRITER) ||
2262176046Smav	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2263176046Smav		rw = NGQRW_W;
2264176046Smav	} else {
2265176046Smav		rw = NGQRW_R;
2266176046Smav	}
2267149505Sglebius
2268175847Smav	/*
2269194012Szec	 * If sender or receiver requests queued delivery, or call graph
2270194012Szec	 * loops back from outbound to inbound path, or stack usage
2271175847Smav	 * level is dangerous - enqueue message.
2272175847Smav	 */
2273175847Smav	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
2274175847Smav		queue = 1;
2275194012Szec	} else if (hook && (hook->hk_flags & HK_TO_INBOUND) &&
2276194012Szec	    curthread->td_ng_outbound) {
2277194012Szec		queue = 1;
2278176046Smav	} else {
2279176046Smav		queue = 0;
2280175847Smav#ifdef GET_STACK_USAGE
2281175868Smav		/*
2282175871Smarck		 * Most of netgraph nodes have small stack consumption and
2283176046Smav		 * for them 25% of free stack space is more than enough.
2284175868Smav		 * Nodes/hooks with higher stack usage should be marked as
2285175889Smarck		 * HI_STACK. For them 50% of stack will be guaranteed then.
2286176046Smav		 * XXX: Values 25% and 50% are completely empirical.
2287175868Smav		 */
2288176046Smav		size_t	st, su, sl;
2289175847Smav		GET_STACK_USAGE(st, su);
2290176046Smav		sl = st - su;
2291229003Sglebius		if ((sl * 4 < st) || ((sl * 2 < st) &&
2292229003Sglebius		    ((node->nd_flags & NGF_HI_STACK) || (hook &&
2293229003Sglebius		    (hook->hk_flags & HK_HI_STACK)))))
2294175847Smav			queue = 1;
2295176046Smav#endif
2296175847Smav	}
2297175847Smav
229870700Sjulian	if (queue) {
229970700Sjulian		/* Put it on the queue for that node*/
2300178228Smav		ng_queue_rw(node, item, rw);
2301176046Smav		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
230270700Sjulian	}
230369922Sjulian
230470700Sjulian	/*
230570700Sjulian	 * We already decided how we will be queueud or treated.
230670700Sjulian	 * Try get the appropriate operating permission.
230770700Sjulian	 */
2308151256Sglebius 	if (rw == NGQRW_R)
2309178228Smav		item = ng_acquire_read(node, item);
2310151256Sglebius	else
2311178228Smav		item = ng_acquire_write(node, item);
231252419Sjulian
2313176046Smav	/* Item was queued while trying to get permission. */
2314176046Smav	if (item == NULL)
2315176046Smav		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
231652419Sjulian
231774078Sjulian	NGI_GET_NODE(item, node); /* zaps stored node */
231852419Sjulian
2319177071Smav	item->depth++;
2320170180Sglebius	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
232174078Sjulian
2322177953Smav	/* If something is waiting on queue and ready, schedule it. */
2323178228Smav	ngq = &node->nd_input_queue;
2324177953Smav	if (QUEUE_ACTIVE(ngq)) {
2325177953Smav		NG_QUEUE_LOCK(ngq);
2326177953Smav		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2327178228Smav			ng_worklist_add(node);
2328177953Smav		NG_QUEUE_UNLOCK(ngq);
2329177953Smav	}
2330177953Smav
233152419Sjulian	/*
2332177953Smav	 * Node may go away as soon as we remove the reference.
2333177953Smav	 * Whatever we do, DO NOT access the node again!
233474078Sjulian	 */
2335177953Smav	NG_NODE_UNREF(node);
233674078Sjulian
233771849Sjulian	return (error);
2338172806Smav
2339172806Smavdone:
2340176046Smav	/* If was not sent, apply callback here. */
2341177071Smav	if (item->apply != NULL) {
2342177071Smav		if (item->depth == 0 && error != 0)
2343177071Smav			item->apply->error = error;
2344177071Smav		if (refcount_release(&item->apply->refs)) {
2345177071Smav			(*item->apply->apply)(item->apply->context,
2346177071Smav			    item->apply->error);
2347177071Smav		}
2348177071Smav	}
2349175850Smav
2350172806Smav	NG_FREE_ITEM(item);
2351172806Smav	return (error);
235252419Sjulian}
235352419Sjulian
235452419Sjulian/*
235570700Sjulian * We have an item that was possibly queued somewhere.
235670700Sjulian * It should contain all the information needed
235770700Sjulian * to run it on the appropriate node/hook.
2358172806Smav * If there is apply pointer and we own the last reference, call apply().
235952419Sjulian */
2360170180Sglebiusstatic int
2361151238Sglebiusng_apply_item(node_p node, item_p item, int rw)
236252419Sjulian{
236370700Sjulian	hook_p  hook;
236470700Sjulian	ng_rcvdata_t *rcvdata;
236571885Sjulian	ng_rcvmsg_t *rcvmsg;
2366172806Smav	struct ng_apply_info *apply;
2367177071Smav	int	error = 0, depth;
236852419Sjulian
2369176046Smav	/* Node and item are never optional. */
2370176046Smav	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2371176046Smav	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2372176046Smav
237371849Sjulian	NGI_GET_HOOK(item, hook); /* clears stored hook */
237470784Sjulian#ifdef	NETGRAPH_DEBUG
2375152451Sglebius	_ngi_check(item, __FILE__, __LINE__);
237670700Sjulian#endif
2377147774Sglebius
2378172806Smav	apply = item->apply;
2379177071Smav	depth = item->depth;
2380147774Sglebius
238171047Sjulian	switch (item->el_flags & NGQF_TYPE) {
238270700Sjulian	case NGQF_DATA:
238370700Sjulian		/*
238470700Sjulian		 * Check things are still ok as when we were queued.
238570700Sjulian		 */
2386176046Smav		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2387176046Smav		if (NG_HOOK_NOT_VALID(hook) ||
2388176046Smav		    NG_NODE_NOT_VALID(node)) {
2389167402Sjulian			error = EIO;
239070700Sjulian			NG_FREE_ITEM(item);
239171885Sjulian			break;
239270700Sjulian		}
239371885Sjulian		/*
239471885Sjulian		 * If no receive method, just silently drop it.
2395229003Sglebius		 * Give preference to the hook over-ride method.
239671885Sjulian		 */
2397229003Sglebius		if ((!(rcvdata = hook->hk_rcvdata)) &&
2398229003Sglebius		    (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
239971885Sjulian			error = 0;
240071885Sjulian			NG_FREE_ITEM(item);
240171885Sjulian			break;
240271885Sjulian		}
2403167402Sjulian		error = (*rcvdata)(hook, item);
240470700Sjulian		break;
240570700Sjulian	case NGQF_MESG:
2406175850Smav		if (hook && NG_HOOK_NOT_VALID(hook)) {
2407175850Smav			/*
2408175850Smav			 * The hook has been zapped then we can't use it.
2409175850Smav			 * Immediately drop its reference.
2410175850Smav			 * The message may not need it.
2411175850Smav			 */
2412175850Smav			NG_HOOK_UNREF(hook);
2413175850Smav			hook = NULL;
241470700Sjulian		}
241570700Sjulian		/*
241670700Sjulian		 * Similarly, if the node is a zombie there is
241770700Sjulian		 * nothing we can do with it, drop everything.
241870700Sjulian		 */
241970784Sjulian		if (NG_NODE_NOT_VALID(node)) {
242071047Sjulian			TRAP_ERROR();
2421167402Sjulian			error = EINVAL;
242270700Sjulian			NG_FREE_ITEM(item);
2423175850Smav			break;
242470700Sjulian		}
2425175850Smav		/*
2426175850Smav		 * Call the appropriate message handler for the object.
2427175850Smav		 * It is up to the message handler to free the message.
2428175850Smav		 * If it's a generic message, handle it generically,
2429175850Smav		 * otherwise call the type's message handler (if it exists).
2430175850Smav		 * XXX (race). Remember that a queued message may
2431175850Smav		 * reference a node or hook that has just been
2432175850Smav		 * invalidated. It will exist as the queue code
2433175850Smav		 * is holding a reference, but..
2434175850Smav		 */
2435175850Smav		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2436175850Smav		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2437175850Smav			error = ng_generic_msg(node, item, hook);
2438175850Smav			break;
2439175850Smav		}
2440175850Smav		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2441175850Smav		    (!(rcvmsg = node->nd_type->rcvmsg))) {
2442175850Smav			TRAP_ERROR();
2443175850Smav			error = 0;
2444175850Smav			NG_FREE_ITEM(item);
2445175850Smav			break;
2446175850Smav		}
2447175850Smav		error = (*rcvmsg)(node, item, hook);
244870700Sjulian		break;
244971047Sjulian	case NGQF_FN:
2450173605Sglebius	case NGQF_FN2:
245171047Sjulian		/*
2452182995Smav		 * In the case of the shutdown message we allow it to hit
245371849Sjulian		 * even if the node is invalid.
245471047Sjulian		 */
2455182995Smav		if (NG_NODE_NOT_VALID(node) &&
2456182995Smav		    NGI_FN(item) != &ng_rmnode) {
245771047Sjulian			TRAP_ERROR();
2458167402Sjulian			error = EINVAL;
2459143384Sglebius			NG_FREE_ITEM(item);
246071047Sjulian			break;
246171047Sjulian		}
2462182995Smav		/* Same is about some internal functions and invalid hook. */
2463182995Smav		if (hook && NG_HOOK_NOT_VALID(hook) &&
2464182995Smav		    NGI_FN2(item) != &ng_con_part2 &&
2465182995Smav		    NGI_FN2(item) != &ng_con_part3 &&
2466182995Smav		    NGI_FN(item) != &ng_rmhook_part2) {
2467182995Smav			TRAP_ERROR();
2468182995Smav			error = EINVAL;
2469182995Smav			NG_FREE_ITEM(item);
2470182995Smav			break;
2471182995Smav		}
2472182995Smav
2473173605Sglebius		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2474173605Sglebius			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2475173605Sglebius			    NGI_ARG2(item));
2476172806Smav			NG_FREE_ITEM(item);
2477173605Sglebius		} else	/* it is NGQF_FN2 */
2478173605Sglebius			error = (*NGI_FN2(item))(node, item, hook);
2479172806Smav		break;
248070700Sjulian	}
248170700Sjulian	/*
248270700Sjulian	 * We held references on some of the resources
248370700Sjulian	 * that we took from the item. Now that we have
248470700Sjulian	 * finished doing everything, drop those references.
248570700Sjulian	 */
2486175850Smav	if (hook)
248770784Sjulian		NG_HOOK_UNREF(hook);
248870700Sjulian
2489176046Smav 	if (rw == NGQRW_R)
2490178228Smav		ng_leave_read(node);
2491176046Smav	else
2492178228Smav		ng_leave_write(node);
2493147774Sglebius
2494147774Sglebius	/* Apply callback. */
2495177071Smav	if (apply != NULL) {
2496177071Smav		if (depth == 1 && error != 0)
2497177071Smav			apply->error = error;
2498177071Smav		if (refcount_release(&apply->refs))
2499177071Smav			(*apply->apply)(apply->context, apply->error);
2500177071Smav	}
2501147774Sglebius
2502170180Sglebius	return (error);
250370700Sjulian}
250470700Sjulian
250570700Sjulian/***********************************************************************
250670700Sjulian * Implement the 'generic' control messages
250770700Sjulian ***********************************************************************/
250870700Sjulianstatic int
250970700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook)
251070700Sjulian{
251170700Sjulian	int error = 0;
251270700Sjulian	struct ng_mesg *msg;
251370700Sjulian	struct ng_mesg *resp = NULL;
251470700Sjulian
251570700Sjulian	NGI_GET_MSG(item, msg);
251652419Sjulian	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
251771047Sjulian		TRAP_ERROR();
251870700Sjulian		error = EINVAL;
251970700Sjulian		goto out;
252052419Sjulian	}
252152419Sjulian	switch (msg->header.cmd) {
252252419Sjulian	case NGM_SHUTDOWN:
252371849Sjulian		ng_rmnode(here, NULL, NULL, 0);
252452419Sjulian		break;
252552419Sjulian	case NGM_MKPEER:
252652419Sjulian	    {
252752419Sjulian		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
252852419Sjulian
252952419Sjulian		if (msg->header.arglen != sizeof(*mkp)) {
253071047Sjulian			TRAP_ERROR();
253170700Sjulian			error = EINVAL;
253270700Sjulian			break;
253352419Sjulian		}
253452419Sjulian		mkp->type[sizeof(mkp->type) - 1] = '\0';
253552419Sjulian		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
253652419Sjulian		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
253752419Sjulian		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
253852419Sjulian		break;
253952419Sjulian	    }
254052419Sjulian	case NGM_CONNECT:
254152419Sjulian	    {
254252419Sjulian		struct ngm_connect *const con =
254352419Sjulian			(struct ngm_connect *) msg->data;
254452419Sjulian		node_p node2;
254552419Sjulian
254652419Sjulian		if (msg->header.arglen != sizeof(*con)) {
254771047Sjulian			TRAP_ERROR();
254870700Sjulian			error = EINVAL;
254970700Sjulian			break;
255052419Sjulian		}
255152419Sjulian		con->path[sizeof(con->path) - 1] = '\0';
255252419Sjulian		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
255352419Sjulian		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
255470700Sjulian		/* Don't forget we get a reference.. */
255570700Sjulian		error = ng_path2noderef(here, con->path, &node2, NULL);
255652419Sjulian		if (error)
255752419Sjulian			break;
2558172806Smav		error = ng_con_nodes(item, here, con->ourhook,
2559172806Smav		    node2, con->peerhook);
256070784Sjulian		NG_NODE_UNREF(node2);
256152419Sjulian		break;
256252419Sjulian	    }
256352419Sjulian	case NGM_NAME:
256452419Sjulian	    {
256552419Sjulian		struct ngm_name *const nam = (struct ngm_name *) msg->data;
256652419Sjulian
256752419Sjulian		if (msg->header.arglen != sizeof(*nam)) {
256871047Sjulian			TRAP_ERROR();
256970700Sjulian			error = EINVAL;
257070700Sjulian			break;
257152419Sjulian		}
257252419Sjulian		nam->name[sizeof(nam->name) - 1] = '\0';
257352419Sjulian		error = ng_name_node(here, nam->name);
257452419Sjulian		break;
257552419Sjulian	    }
257652419Sjulian	case NGM_RMHOOK:
257752419Sjulian	    {
257852419Sjulian		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
257952419Sjulian		hook_p hook;
258052419Sjulian
258152419Sjulian		if (msg->header.arglen != sizeof(*rmh)) {
258271047Sjulian			TRAP_ERROR();
258370700Sjulian			error = EINVAL;
258470700Sjulian			break;
258552419Sjulian		}
258652419Sjulian		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
258754096Sarchie		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
258852419Sjulian			ng_destroy_hook(hook);
258952419Sjulian		break;
259052419Sjulian	    }
259152419Sjulian	case NGM_NODEINFO:
259252419Sjulian	    {
259352419Sjulian		struct nodeinfo *ni;
259452419Sjulian
259570700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
259652419Sjulian		if (resp == NULL) {
259752419Sjulian			error = ENOMEM;
259852419Sjulian			break;
259952419Sjulian		}
260052419Sjulian
260152419Sjulian		/* Fill in node info */
260270700Sjulian		ni = (struct nodeinfo *) resp->data;
260370784Sjulian		if (NG_NODE_HAS_NAME(here))
2604125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2605125028Sharti		strcpy(ni->type, here->nd_type->name);
260652722Sjulian		ni->id = ng_node2ID(here);
260770784Sjulian		ni->hooks = here->nd_numhooks;
260852419Sjulian		break;
260952419Sjulian	    }
261052419Sjulian	case NGM_LISTHOOKS:
261152419Sjulian	    {
261270784Sjulian		const int nhooks = here->nd_numhooks;
261352419Sjulian		struct hooklist *hl;
261452419Sjulian		struct nodeinfo *ni;
261552419Sjulian		hook_p hook;
261652419Sjulian
261752419Sjulian		/* Get response struct */
2618229003Sglebius		NG_MKRESPONSE(resp, msg, sizeof(*hl) +
2619229003Sglebius		    (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
262052419Sjulian		if (resp == NULL) {
262152419Sjulian			error = ENOMEM;
262252419Sjulian			break;
262352419Sjulian		}
262470700Sjulian		hl = (struct hooklist *) resp->data;
262552419Sjulian		ni = &hl->nodeinfo;
262652419Sjulian
262752419Sjulian		/* Fill in node info */
262870784Sjulian		if (NG_NODE_HAS_NAME(here))
2629125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2630125028Sharti		strcpy(ni->type, here->nd_type->name);
263152722Sjulian		ni->id = ng_node2ID(here);
263252419Sjulian
263352419Sjulian		/* Cycle through the linked list of hooks */
263452419Sjulian		ni->hooks = 0;
263570784Sjulian		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
263652419Sjulian			struct linkinfo *const link = &hl->link[ni->hooks];
263752419Sjulian
263852419Sjulian			if (ni->hooks >= nhooks) {
263952419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
264087599Sobrien				    __func__, "hooks");
264152419Sjulian				break;
264252419Sjulian			}
264370784Sjulian			if (NG_HOOK_NOT_VALID(hook))
264452419Sjulian				continue;
2645125028Sharti			strcpy(link->ourhook, NG_HOOK_NAME(hook));
2646125028Sharti			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
264770784Sjulian			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
2648125028Sharti				strcpy(link->nodeinfo.name,
2649125028Sharti				    NG_PEER_NODE_NAME(hook));
2650125028Sharti			strcpy(link->nodeinfo.type,
2651125028Sharti			   NG_PEER_NODE(hook)->nd_type->name);
265270784Sjulian			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
265370784Sjulian			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
265452419Sjulian			ni->hooks++;
265552419Sjulian		}
265652419Sjulian		break;
265752419Sjulian	    }
265852419Sjulian
265952419Sjulian	case NGM_LISTNODES:
266052419Sjulian	    {
266152419Sjulian		struct namelist *nl;
266252419Sjulian		node_p node;
2663231831Sglebius		int i;
266452419Sjulian
2665231831Sglebius		IDHASH_RLOCK();
2666231831Sglebius		/* Get response struct. */
2667231831Sglebius		NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2668338333Smav		    (V_ng_nodes * sizeof(struct nodeinfo)), M_NOWAIT);
2669231831Sglebius		if (resp == NULL) {
2670231831Sglebius			IDHASH_RUNLOCK();
2671231831Sglebius			error = ENOMEM;
2672231831Sglebius			break;
2673231831Sglebius		}
2674231831Sglebius		nl = (struct namelist *) resp->data;
2675231831Sglebius
2676231831Sglebius		/* Cycle through the lists of nodes. */
2677231831Sglebius		nl->numnames = 0;
2678231831Sglebius		for (i = 0; i <= V_ng_ID_hmask; i++) {
2679231831Sglebius			LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
2680231831Sglebius				struct nodeinfo *const np =
2681231831Sglebius				    &nl->nodeinfo[nl->numnames];
2682231831Sglebius
2683231831Sglebius				if (NG_NODE_NOT_VALID(node))
2684231831Sglebius					continue;
2685231831Sglebius				if (NG_NODE_HAS_NAME(node))
2686231831Sglebius					strcpy(np->name, NG_NODE_NAME(node));
2687231831Sglebius				strcpy(np->type, node->nd_type->name);
2688231831Sglebius				np->id = ng_node2ID(node);
2689231831Sglebius				np->hooks = node->nd_numhooks;
2690231831Sglebius				KASSERT(nl->numnames < V_ng_nodes,
2691231831Sglebius				    ("%s: no space", __func__));
2692231831Sglebius				nl->numnames++;
269370912Sjulian			}
269452419Sjulian		}
2695231831Sglebius		IDHASH_RUNLOCK();
2696231831Sglebius		break;
2697231831Sglebius	    }
2698231831Sglebius	case NGM_LISTNAMES:
2699231831Sglebius	    {
2700231831Sglebius		struct namelist *nl;
2701231831Sglebius		node_p node;
2702231831Sglebius		int i;
270352419Sjulian
2704231831Sglebius		NAMEHASH_RLOCK();
2705231831Sglebius		/* Get response struct. */
2706229003Sglebius		NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2707231831Sglebius		    (V_ng_named_nodes * sizeof(struct nodeinfo)), M_NOWAIT);
270852419Sjulian		if (resp == NULL) {
2709230480Sglebius			NAMEHASH_RUNLOCK();
271052419Sjulian			error = ENOMEM;
271152419Sjulian			break;
271252419Sjulian		}
271370700Sjulian		nl = (struct namelist *) resp->data;
271452419Sjulian
2715231831Sglebius		/* Cycle through the lists of nodes. */
271652419Sjulian		nl->numnames = 0;
2717231831Sglebius		for (i = 0; i <= V_ng_name_hmask; i++) {
2718181803Sbz			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2719176802Smav				struct nodeinfo *const np =
2720176802Smav				    &nl->nodeinfo[nl->numnames];
272152419Sjulian
2722176802Smav				if (NG_NODE_NOT_VALID(node))
2723176802Smav					continue;
2724231831Sglebius				strcpy(np->name, NG_NODE_NAME(node));
2725176802Smav				strcpy(np->type, node->nd_type->name);
2726176802Smav				np->id = ng_node2ID(node);
2727176802Smav				np->hooks = node->nd_numhooks;
2728231831Sglebius				KASSERT(nl->numnames < V_ng_named_nodes,
2729231831Sglebius				    ("%s: no space", __func__));
2730176802Smav				nl->numnames++;
273152419Sjulian			}
273252419Sjulian		}
2733230480Sglebius		NAMEHASH_RUNLOCK();
273452419Sjulian		break;
273552419Sjulian	    }
273652419Sjulian
273752419Sjulian	case NGM_LISTTYPES:
273852419Sjulian	    {
273952419Sjulian		struct typelist *tl;
274052419Sjulian		struct ng_type *type;
274152419Sjulian		int num = 0;
274252419Sjulian
2743230480Sglebius		TYPELIST_RLOCK();
274452419Sjulian		/* Count number of types */
2745230480Sglebius		LIST_FOREACH(type, &ng_typelist, types)
274652419Sjulian			num++;
274752419Sjulian
274852419Sjulian		/* Get response struct */
2749229003Sglebius		NG_MKRESPONSE(resp, msg, sizeof(*tl) +
2750229003Sglebius		    (num * sizeof(struct typeinfo)), M_NOWAIT);
275152419Sjulian		if (resp == NULL) {
2752230480Sglebius			TYPELIST_RUNLOCK();
275352419Sjulian			error = ENOMEM;
275452419Sjulian			break;
275552419Sjulian		}
275670700Sjulian		tl = (struct typelist *) resp->data;
275752419Sjulian
275852419Sjulian		/* Cycle through the linked list of types */
275952419Sjulian		tl->numtypes = 0;
276070700Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
276152419Sjulian			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
276252419Sjulian
2763125028Sharti			strcpy(tp->type_name, type->name);
276471603Sjulian			tp->numnodes = type->refs - 1; /* don't count list */
2765230480Sglebius			KASSERT(tl->numtypes < num, ("%s: no space", __func__));
276652419Sjulian			tl->numtypes++;
276752419Sjulian		}
2768230480Sglebius		TYPELIST_RUNLOCK();
276952419Sjulian		break;
277052419Sjulian	    }
277152419Sjulian
277253913Sarchie	case NGM_BINARY2ASCII:
277353913Sarchie	    {
277464510Sarchie		int bufSize = 20 * 1024;	/* XXX hard coded constant */
277553913Sarchie		const struct ng_parse_type *argstype;
277653913Sarchie		const struct ng_cmdlist *c;
277770700Sjulian		struct ng_mesg *binary, *ascii;
277853913Sarchie
277953913Sarchie		/* Data area must contain a valid netgraph message */
278053913Sarchie		binary = (struct ng_mesg *)msg->data;
2781152451Sglebius		if (msg->header.arglen < sizeof(struct ng_mesg) ||
2782152451Sglebius		    (msg->header.arglen - sizeof(struct ng_mesg) <
2783152451Sglebius		    binary->header.arglen)) {
278471047Sjulian			TRAP_ERROR();
278553913Sarchie			error = EINVAL;
278653913Sarchie			break;
278753913Sarchie		}
278853913Sarchie
278970700Sjulian		/* Get a response message with lots of room */
279070700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
279170159Sjulian		if (resp == NULL) {
279253913Sarchie			error = ENOMEM;
279353913Sarchie			break;
279453913Sarchie		}
279570700Sjulian		ascii = (struct ng_mesg *)resp->data;
279653913Sarchie
279753913Sarchie		/* Copy binary message header to response message payload */
279853913Sarchie		bcopy(binary, ascii, sizeof(*binary));
279953913Sarchie
280053913Sarchie		/* Find command by matching typecookie and command number */
2801229003Sglebius		for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL;
2802229003Sglebius		    c++) {
2803229003Sglebius			if (binary->header.typecookie == c->cookie &&
2804229003Sglebius			    binary->header.cmd == c->cmd)
280553913Sarchie				break;
280653913Sarchie		}
280753913Sarchie		if (c == NULL || c->name == NULL) {
280853913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
2809229003Sglebius				if (binary->header.typecookie == c->cookie &&
2810229003Sglebius				    binary->header.cmd == c->cmd)
281153913Sarchie					break;
281253913Sarchie			}
281353913Sarchie			if (c->name == NULL) {
281470700Sjulian				NG_FREE_MSG(resp);
281553913Sarchie				error = ENOSYS;
281653913Sarchie				break;
281753913Sarchie			}
281853913Sarchie		}
281953913Sarchie
282053913Sarchie		/* Convert command name to ASCII */
282153913Sarchie		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
282253913Sarchie		    "%s", c->name);
282353913Sarchie
282453913Sarchie		/* Convert command arguments to ASCII */
282553913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
282653913Sarchie		    c->respType : c->mesgType;
282770912Sjulian		if (argstype == NULL) {
282853913Sarchie			*ascii->data = '\0';
282970912Sjulian		} else {
283053913Sarchie			if ((error = ng_unparse(argstype,
283153913Sarchie			    (u_char *)binary->data,
283253913Sarchie			    ascii->data, bufSize)) != 0) {
283370700Sjulian				NG_FREE_MSG(resp);
283453913Sarchie				break;
283553913Sarchie			}
283653913Sarchie		}
283753913Sarchie
283853913Sarchie		/* Return the result as struct ng_mesg plus ASCII string */
283953913Sarchie		bufSize = strlen(ascii->data) + 1;
284053913Sarchie		ascii->header.arglen = bufSize;
284170700Sjulian		resp->header.arglen = sizeof(*ascii) + bufSize;
284253913Sarchie		break;
284353913Sarchie	    }
284453913Sarchie
284553913Sarchie	case NGM_ASCII2BINARY:
284653913Sarchie	    {
2847208036Szec		int bufSize = 20 * 1024;	/* XXX hard coded constant */
284853913Sarchie		const struct ng_cmdlist *c;
284953913Sarchie		const struct ng_parse_type *argstype;
285070700Sjulian		struct ng_mesg *ascii, *binary;
285159178Sarchie		int off = 0;
285253913Sarchie
285353913Sarchie		/* Data area must contain at least a struct ng_mesg + '\0' */
285453913Sarchie		ascii = (struct ng_mesg *)msg->data;
2855152451Sglebius		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
2856152451Sglebius		    (ascii->header.arglen < 1) ||
2857152451Sglebius		    (msg->header.arglen < sizeof(*ascii) +
2858152451Sglebius		    ascii->header.arglen)) {
285971047Sjulian			TRAP_ERROR();
286053913Sarchie			error = EINVAL;
286153913Sarchie			break;
286253913Sarchie		}
286353913Sarchie		ascii->data[ascii->header.arglen - 1] = '\0';
286453913Sarchie
286570700Sjulian		/* Get a response message with lots of room */
286670700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
286770159Sjulian		if (resp == NULL) {
286853913Sarchie			error = ENOMEM;
286953913Sarchie			break;
287053913Sarchie		}
287170700Sjulian		binary = (struct ng_mesg *)resp->data;
287253913Sarchie
287353913Sarchie		/* Copy ASCII message header to response message payload */
287453913Sarchie		bcopy(ascii, binary, sizeof(*ascii));
287553913Sarchie
287653913Sarchie		/* Find command by matching ASCII command string */
287770784Sjulian		for (c = here->nd_type->cmdlist;
287853913Sarchie		    c != NULL && c->name != NULL; c++) {
287953913Sarchie			if (strcmp(ascii->header.cmdstr, c->name) == 0)
288053913Sarchie				break;
288153913Sarchie		}
288253913Sarchie		if (c == NULL || c->name == NULL) {
288353913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
288453913Sarchie				if (strcmp(ascii->header.cmdstr, c->name) == 0)
288553913Sarchie					break;
288653913Sarchie			}
288753913Sarchie			if (c->name == NULL) {
288870700Sjulian				NG_FREE_MSG(resp);
288953913Sarchie				error = ENOSYS;
289053913Sarchie				break;
289153913Sarchie			}
289253913Sarchie		}
289353913Sarchie
289453913Sarchie		/* Convert command name to binary */
289553913Sarchie		binary->header.cmd = c->cmd;
289653913Sarchie		binary->header.typecookie = c->cookie;
289753913Sarchie
289853913Sarchie		/* Convert command arguments to binary */
289953913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
290053913Sarchie		    c->respType : c->mesgType;
290170912Sjulian		if (argstype == NULL) {
290253913Sarchie			bufSize = 0;
290370912Sjulian		} else {
2904229003Sglebius			if ((error = ng_parse(argstype, ascii->data, &off,
2905229003Sglebius			    (u_char *)binary->data, &bufSize)) != 0) {
290670700Sjulian				NG_FREE_MSG(resp);
290753913Sarchie				break;
290853913Sarchie			}
290953913Sarchie		}
291053913Sarchie
291153913Sarchie		/* Return the result */
291253913Sarchie		binary->header.arglen = bufSize;
291370700Sjulian		resp->header.arglen = sizeof(*binary) + bufSize;
291453913Sarchie		break;
291553913Sarchie	    }
291653913Sarchie
291762471Sphk	case NGM_TEXT_CONFIG:
291852419Sjulian	case NGM_TEXT_STATUS:
291952419Sjulian		/*
292052419Sjulian		 * This one is tricky as it passes the command down to the
292152419Sjulian		 * actual node, even though it is a generic type command.
292270700Sjulian		 * This means we must assume that the item/msg is already freed
292352419Sjulian		 * when control passes back to us.
292452419Sjulian		 */
292570784Sjulian		if (here->nd_type->rcvmsg != NULL) {
292670700Sjulian			NGI_MSG(item) = msg; /* put it back as we found it */
292770784Sjulian			return((*here->nd_type->rcvmsg)(here, item, lasthook));
292852419Sjulian		}
292952419Sjulian		/* Fall through if rcvmsg not supported */
293052419Sjulian	default:
293171047Sjulian		TRAP_ERROR();
293252419Sjulian		error = EINVAL;
293352419Sjulian	}
293470700Sjulian	/*
293570700Sjulian	 * Sometimes a generic message may be statically allocated
2936229003Sglebius	 * to avoid problems with allocating when in tight memory situations.
293770700Sjulian	 * Don't free it if it is so.
2938298813Spfg	 * I break them apart here, because erros may cause a free if the item
293970700Sjulian	 * in which case we'd be doing it twice.
294070700Sjulian	 * they are kept together above, to simplify freeing.
294170700Sjulian	 */
294270700Sjulianout:
294370700Sjulian	NG_RESPOND_MSG(error, here, item, resp);
2944185179Smav	NG_FREE_MSG(msg);
294552419Sjulian	return (error);
294652419Sjulian}
294752419Sjulian
294852419Sjulian/************************************************************************
2949146213Sglebius			Queue element get/free routines
2950146213Sglebius************************************************************************/
2951146213Sglebius
2952146213Sglebiusuma_zone_t			ng_qzone;
2953178259Smavuma_zone_t			ng_qdzone;
2954186093Smavstatic int			numthreads = 0; /* number of queue threads */
2955176849Smavstatic int			maxalloc = 4096;/* limit the damage of a leak */
2956278640Sglebiusstatic int			maxdata = 4096;	/* limit the damage of a DoS */
2957146213Sglebius
2958186093SmavSYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads,
2959186093Smav    0, "Number of queue processing threads");
2960146213SglebiusSYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
2961178259Smav    0, "Maximum number of non-data queue items to allocate");
2962178259SmavSYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
2963178259Smav    0, "Maximum number of data queue items to allocate");
2964146213Sglebius
2965146213Sglebius#ifdef	NETGRAPH_DEBUG
2966146213Sglebiusstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
2967229003Sglebiusstatic int allocated;	/* number of items malloc'd */
2968146213Sglebius#endif
2969146213Sglebius
2970146213Sglebius/*
2971146213Sglebius * Get a queue entry.
2972146213Sglebius * This is usually called when a packet first enters netgraph.
2973146213Sglebius * By definition, this is usually from an interrupt, or from a user.
2974146213Sglebius * Users are not so important, but try be quick for the times that it's
2975146213Sglebius * an interrupt.
2976146213Sglebius */
2977146213Sglebiusstatic __inline item_p
2978178259Smavng_alloc_item(int type, int flags)
2979146213Sglebius{
2980178259Smav	item_p item;
2981146213Sglebius
2982178259Smav	KASSERT(((type & ~NGQF_TYPE) == 0),
2983178259Smav	    ("%s: incorrect item type: %d", __func__, type));
2984146213Sglebius
2985229003Sglebius	item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone,
2986178259Smav	    ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
2987146281Sglebius
2988178259Smav	if (item) {
2989178259Smav		item->el_flags = type;
2990146213Sglebius#ifdef	NETGRAPH_DEBUG
2991178259Smav		mtx_lock(&ngq_mtx);
2992178259Smav		TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
2993178259Smav		allocated++;
2994178259Smav		mtx_unlock(&ngq_mtx);
2995178259Smav#endif
2996146213Sglebius	}
2997146213Sglebius
2998146213Sglebius	return (item);
2999146213Sglebius}
3000146213Sglebius
3001146213Sglebius/*
3002146213Sglebius * Release a queue entry
3003146213Sglebius */
3004146213Sglebiusvoid
3005146213Sglebiusng_free_item(item_p item)
3006146213Sglebius{
3007146213Sglebius	/*
3008146213Sglebius	 * The item may hold resources on it's own. We need to free
3009146213Sglebius	 * these before we can free the item. What they are depends upon
3010146213Sglebius	 * what kind of item it is. it is important that nodes zero
3011146213Sglebius	 * out pointers to resources that they remove from the item
3012146213Sglebius	 * or we release them again here.
3013146213Sglebius	 */
3014146213Sglebius	switch (item->el_flags & NGQF_TYPE) {
3015146213Sglebius	case NGQF_DATA:
3016146213Sglebius		/* If we have an mbuf still attached.. */
3017146213Sglebius		NG_FREE_M(_NGI_M(item));
3018146213Sglebius		break;
3019146213Sglebius	case NGQF_MESG:
3020146213Sglebius		_NGI_RETADDR(item) = 0;
3021146213Sglebius		NG_FREE_MSG(_NGI_MSG(item));
3022146213Sglebius		break;
3023146213Sglebius	case NGQF_FN:
3024172806Smav	case NGQF_FN2:
3025146213Sglebius		/* nothing to free really, */
3026146213Sglebius		_NGI_FN(item) = NULL;
3027146213Sglebius		_NGI_ARG1(item) = NULL;
3028146213Sglebius		_NGI_ARG2(item) = 0;
3029146213Sglebius		break;
3030146213Sglebius	}
3031146213Sglebius	/* If we still have a node or hook referenced... */
3032146213Sglebius	_NGI_CLR_NODE(item);
3033146213Sglebius	_NGI_CLR_HOOK(item);
3034146213Sglebius
3035146213Sglebius#ifdef	NETGRAPH_DEBUG
3036146213Sglebius	mtx_lock(&ngq_mtx);
3037146213Sglebius	TAILQ_REMOVE(&ng_itemlist, item, all);
3038146213Sglebius	allocated--;
3039146213Sglebius	mtx_unlock(&ngq_mtx);
3040146213Sglebius#endif
3041229003Sglebius	uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ?
3042229003Sglebius	    ng_qdzone : ng_qzone, item);
3043146213Sglebius}
3044146213Sglebius
3045178259Smav/*
3046178259Smav * Change type of the queue entry.
3047178259Smav * Possibly reallocates it from another UMA zone.
3048178259Smav */
3049178259Smavstatic __inline item_p
3050178259Smavng_realloc_item(item_p pitem, int type, int flags)
3051178259Smav{
3052178259Smav	item_p item;
3053178259Smav	int from, to;
3054178259Smav
3055178259Smav	KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
3056178259Smav	KASSERT(((type & ~NGQF_TYPE) == 0),
3057178259Smav	    ("%s: incorrect item type: %d", __func__, type));
3058178259Smav
3059178259Smav	from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
3060178259Smav	to = (type == NGQF_DATA);
3061178259Smav	if (from != to) {
3062178259Smav		/* If reallocation is required do it and copy item. */
3063178259Smav		if ((item = ng_alloc_item(type, flags)) == NULL) {
3064178259Smav			ng_free_item(pitem);
3065178259Smav			return (NULL);
3066178259Smav		}
3067178259Smav		*item = *pitem;
3068178259Smav		ng_free_item(pitem);
3069178259Smav	} else
3070178259Smav		item = pitem;
3071178259Smav	item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
3072178259Smav
3073178259Smav	return (item);
3074178259Smav}
3075178259Smav
3076146213Sglebius/************************************************************************
307752419Sjulian			Module routines
307852419Sjulian************************************************************************/
307952419Sjulian
308052419Sjulian/*
308152419Sjulian * Handle the loading/unloading of a netgraph node type module
308252419Sjulian */
308352419Sjulianint
308452419Sjulianng_mod_event(module_t mod, int event, void *data)
308552419Sjulian{
308652419Sjulian	struct ng_type *const type = data;
3087229003Sglebius	int error = 0;
308852419Sjulian
308952419Sjulian	switch (event) {
309052419Sjulian	case MOD_LOAD:
309152419Sjulian
309252419Sjulian		/* Register new netgraph node type */
3093229003Sglebius		if ((error = ng_newtype(type)) != 0)
309452419Sjulian			break;
309552419Sjulian
309652419Sjulian		/* Call type specific code */
309752419Sjulian		if (type->mod_event != NULL)
309870700Sjulian			if ((error = (*type->mod_event)(mod, event, data))) {
3099230480Sglebius				TYPELIST_WLOCK();
310071603Sjulian				type->refs--;	/* undo it */
310152419Sjulian				LIST_REMOVE(type, types);
3102230480Sglebius				TYPELIST_WUNLOCK();
310370700Sjulian			}
310452419Sjulian		break;
310552419Sjulian
310652419Sjulian	case MOD_UNLOAD:
310771603Sjulian		if (type->refs > 1) {		/* make sure no nodes exist! */
310852419Sjulian			error = EBUSY;
310971603Sjulian		} else {
3110229003Sglebius			if (type->refs == 0) /* failed load, nothing to undo */
311171603Sjulian				break;
311252419Sjulian			if (type->mod_event != NULL) {	/* check with type */
311352419Sjulian				error = (*type->mod_event)(mod, event, data);
3114229003Sglebius				if (error != 0)	/* type refuses.. */
311552419Sjulian					break;
311652419Sjulian			}
3117230480Sglebius			TYPELIST_WLOCK();
311852419Sjulian			LIST_REMOVE(type, types);
3119230480Sglebius			TYPELIST_WUNLOCK();
312052419Sjulian		}
312152419Sjulian		break;
312252419Sjulian
312352419Sjulian	default:
312452419Sjulian		if (type->mod_event != NULL)
312552419Sjulian			error = (*type->mod_event)(mod, event, data);
312652419Sjulian		else
3127132199Sphk			error = EOPNOTSUPP;		/* XXX ? */
312852419Sjulian		break;
312952419Sjulian	}
313052419Sjulian	return (error);
313152419Sjulian}
313252419Sjulian
3133231831Sglebiusstatic void
3134231831Sglebiusvnet_netgraph_init(const void *unused __unused)
3135231831Sglebius{
3136231831Sglebius
3137231831Sglebius	/* We start with small hashes, but they can grow. */
3138231831Sglebius	V_ng_ID_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_ID_hmask);
3139231831Sglebius	V_ng_name_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_name_hmask);
3140231831Sglebius}
3141231831SglebiusVNET_SYSINIT(vnet_netgraph_init, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
3142231831Sglebius    vnet_netgraph_init, NULL);
3143231831Sglebius
3144229003Sglebius#ifdef VIMAGE
3145195837Srwatsonstatic void
3146195837Srwatsonvnet_netgraph_uninit(const void *unused __unused)
3147193731Szec{
3148207572Szec	node_p node = NULL, last_killed = NULL;
3149207572Szec	int i;
3150193731Szec
3151207572Szec	do {
3152207572Szec		/* Find a node to kill */
3153231831Sglebius		IDHASH_RLOCK();
3154231831Sglebius		for (i = 0; i <= V_ng_ID_hmask; i++) {
3155231831Sglebius			LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
3156207572Szec				if (node != &ng_deadnode) {
3157207572Szec					NG_NODE_REF(node);
3158207572Szec					break;
3159207572Szec				}
3160207572Szec			}
3161207572Szec			if (node != NULL)
3162207572Szec				break;
3163207572Szec		}
3164231831Sglebius		IDHASH_RUNLOCK();
3165207572Szec
3166207572Szec		/* Attempt to kill it only if it is a regular node */
3167207572Szec		if (node != NULL) {
3168207572Szec			if (node == last_killed) {
3169207572Szec				/* This should never happen */
3170229003Sglebius				printf("ng node %s needs NGF_REALLY_DIE\n",
3171229003Sglebius				    node->nd_name);
3172207572Szec				if (node->nd_flags & NGF_REALLY_DIE)
3173207572Szec					panic("ng node %s won't die",
3174207572Szec					    node->nd_name);
3175207572Szec				node->nd_flags |= NGF_REALLY_DIE;
3176207572Szec			}
3177193731Szec			ng_rmnode(node, NULL, NULL, 0);
3178207572Szec			NG_NODE_UNREF(node);
3179207572Szec			last_killed = node;
3180193731Szec		}
3181207572Szec	} while (node != NULL);
3182231831Sglebius
3183231831Sglebius	hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
3184231831Sglebius	hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_ID_hmask);
3185193731Szec}
3186231830SglebiusVNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
3187195837Srwatson    vnet_netgraph_uninit, NULL);
3188193731Szec#endif /* VIMAGE */
3189193731Szec
319052419Sjulian/*
319152419Sjulian * Handle loading and unloading for this code.
319252419Sjulian * The only thing we need to link into is the NETISR strucure.
319352419Sjulian */
319452419Sjulianstatic int
319552419Sjulianngb_mod_event(module_t mod, int event, void *data)
319652419Sjulian{
3197186093Smav	struct proc *p;
3198186093Smav	struct thread *td;
3199186093Smav	int i, error = 0;
320052419Sjulian
320152419Sjulian	switch (event) {
320252419Sjulian	case MOD_LOAD:
3203146212Sglebius		/* Initialize everything. */
3204168049Swkoszek		NG_WORKLIST_LOCK_INIT();
3205230480Sglebius		rw_init(&ng_typelist_lock, "netgraph types");
3206230480Sglebius		rw_init(&ng_idhash_lock, "netgraph idhash");
3207230480Sglebius		rw_init(&ng_namehash_lock, "netgraph namehash");
3208256550Smelifaro		rw_init(&ng_topo_lock, "netgraph topology mutex");
3209146212Sglebius#ifdef	NETGRAPH_DEBUG
3210176802Smav		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3211176802Smav		    MTX_DEF);
3212146212Sglebius		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3213123278Struckman		    MTX_DEF);
3214146212Sglebius#endif
3215146212Sglebius		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
3216231997Sglebius		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
3217146212Sglebius		uma_zone_set_max(ng_qzone, maxalloc);
3218229003Sglebius		ng_qdzone = uma_zcreate("NetGraph data items",
3219229003Sglebius		    sizeof(struct ng_item), NULL, NULL, NULL, NULL,
3220231997Sglebius		    UMA_ALIGN_CACHE, 0);
3221178259Smav		uma_zone_set_max(ng_qdzone, maxdata);
3222186093Smav		/* Autoconfigure number of threads. */
3223186093Smav		if (numthreads <= 0)
3224186093Smav			numthreads = mp_ncpus;
3225186093Smav		/* Create threads. */
3226186093Smav    		p = NULL; /* start with no process */
3227186093Smav		for (i = 0; i < numthreads; i++) {
3228186093Smav			if (kproc_kthread_add(ngthread, NULL, &p, &td,
3229186093Smav			    RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) {
3230186093Smav				numthreads = i;
3231186093Smav				break;
3232186093Smav			}
3233186093Smav		}
323452419Sjulian		break;
323552419Sjulian	case MOD_UNLOAD:
3236167677Srwatson		/* You can't unload it because an interface may be using it. */
323752419Sjulian		error = EBUSY;
323852419Sjulian		break;
323952419Sjulian	default:
324052419Sjulian		error = EOPNOTSUPP;
324152419Sjulian		break;
324252419Sjulian	}
324352419Sjulian	return (error);
324452419Sjulian}
324552419Sjulian
324652419Sjulianstatic moduledata_t netgraph_mod = {
324752419Sjulian	"netgraph",
324852419Sjulian	ngb_mod_event,
324952419Sjulian	(NULL)
325052419Sjulian};
3251231830SglebiusDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_FIRST);
325272946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
3253273377ShselaskySYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_ABI_VERSION,"");
3254273377ShselaskySYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_VERSION, "");
325552419Sjulian
325670784Sjulian#ifdef	NETGRAPH_DEBUG
325770700Sjulianvoid
325870784Sjuliandumphook (hook_p hook, char *file, int line)
325970784Sjulian{
326070784Sjulian	printf("hook: name %s, %d refs, Last touched:\n",
326170784Sjulian		_NG_HOOK_NAME(hook), hook->hk_refs);
326270784Sjulian	printf("	Last active @ %s, line %d\n",
326370784Sjulian		hook->lastfile, hook->lastline);
326470784Sjulian	if (line) {
326570784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
3266226829Sglebius#ifdef KDB
3267226829Sglebius		kdb_backtrace();
3268226829Sglebius#endif
326970784Sjulian	}
327070784Sjulian}
327170784Sjulian
327270784Sjulianvoid
327370784Sjuliandumpnode(node_p node, char *file, int line)
327470784Sjulian{
327570784Sjulian	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
327671047Sjulian		_NG_NODE_ID(node), node->nd_type->name,
327770784Sjulian		node->nd_numhooks, node->nd_flags,
327870784Sjulian		node->nd_refs, node->nd_name);
327970784Sjulian	printf("	Last active @ %s, line %d\n",
328070784Sjulian		node->lastfile, node->lastline);
328170784Sjulian	if (line) {
328270784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
3283226829Sglebius#ifdef KDB
3284226829Sglebius		kdb_backtrace();
3285226829Sglebius#endif
328670784Sjulian	}
328770784Sjulian}
328870784Sjulian
328970784Sjulianvoid
329070700Sjuliandumpitem(item_p item, char *file, int line)
329170700Sjulian{
3292146212Sglebius	printf(" ACTIVE item, last used at %s, line %d",
3293146212Sglebius		item->lastfile, item->lastline);
3294146212Sglebius	switch(item->el_flags & NGQF_TYPE) {
3295146212Sglebius	case NGQF_DATA:
3296146212Sglebius		printf(" - [data]\n");
3297146212Sglebius		break;
3298146212Sglebius	case NGQF_MESG:
3299146212Sglebius		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
3300146212Sglebius		break;
3301146212Sglebius	case NGQF_FN:
3302172820Sru		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3303172820Sru			_NGI_FN(item),
3304172820Sru			_NGI_NODE(item),
3305172820Sru			_NGI_HOOK(item),
3306172820Sru			item->body.fn.fn_arg1,
3307172820Sru			item->body.fn.fn_arg2,
3308172820Sru			item->body.fn.fn_arg2);
3309172820Sru		break;
3310172806Smav	case NGQF_FN2:
3311173110Smav		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3312172820Sru			_NGI_FN2(item),
3313149735Sglebius			_NGI_NODE(item),
3314149735Sglebius			_NGI_HOOK(item),
3315146212Sglebius			item->body.fn.fn_arg1,
3316146212Sglebius			item->body.fn.fn_arg2,
3317146212Sglebius			item->body.fn.fn_arg2);
3318146212Sglebius		break;
331952419Sjulian	}
332070784Sjulian	if (line) {
332170784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
3322149735Sglebius		if (_NGI_NODE(item)) {
332370784Sjulian			printf("node %p ([%x])\n",
3324149735Sglebius				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
332570784Sjulian		}
332670784Sjulian	}
332770700Sjulian}
332852419Sjulian
332970784Sjulianstatic void
333070784Sjulianng_dumpitems(void)
333170784Sjulian{
333270784Sjulian	item_p item;
333370784Sjulian	int i = 1;
333470784Sjulian	TAILQ_FOREACH(item, &ng_itemlist, all) {
333570784Sjulian		printf("[%d] ", i++);
333670784Sjulian		dumpitem(item, NULL, 0);
333770784Sjulian	}
333870784Sjulian}
333970784Sjulian
334070784Sjulianstatic void
334170784Sjulianng_dumpnodes(void)
334270784Sjulian{
334370784Sjulian	node_p node;
334470784Sjulian	int i = 1;
3345131008Srwatson	mtx_lock(&ng_nodelist_mtx);
334670784Sjulian	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
334770784Sjulian		printf("[%d] ", i++);
334870784Sjulian		dumpnode(node, NULL, 0);
334970784Sjulian	}
3350131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
335170784Sjulian}
335270784Sjulian
335370784Sjulianstatic void
335470784Sjulianng_dumphooks(void)
335570784Sjulian{
335670784Sjulian	hook_p hook;
335770784Sjulian	int i = 1;
3358131008Srwatson	mtx_lock(&ng_nodelist_mtx);
335970784Sjulian	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
336070784Sjulian		printf("[%d] ", i++);
336170784Sjulian		dumphook(hook, NULL, 0);
336270784Sjulian	}
3363131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
336470784Sjulian}
336570784Sjulian
336670700Sjulianstatic int
336770700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
336870700Sjulian{
336970700Sjulian	int error;
337070700Sjulian	int val;
337170700Sjulian	int i;
337270700Sjulian
337370700Sjulian	val = allocated;
337470700Sjulian	i = 1;
3375170289Sdwmalone	error = sysctl_handle_int(oidp, &val, 0, req);
337671047Sjulian	if (error != 0 || req->newptr == NULL)
337771047Sjulian		return (error);
337871047Sjulian	if (val == 42) {
337970784Sjulian		ng_dumpitems();
338070784Sjulian		ng_dumpnodes();
338170784Sjulian		ng_dumphooks();
338270700Sjulian	}
338371047Sjulian	return (0);
338452419Sjulian}
338552419Sjulian
338671047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
338771047Sjulian    0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
338870784Sjulian#endif	/* NETGRAPH_DEBUG */
338970700Sjulian
339070700Sjulian/***********************************************************************
339170700Sjulian* Worklist routines
339270700Sjulian**********************************************************************/
339352419Sjulian/*
339470700Sjulian * Pick a node off the list of nodes with work,
3395186093Smav * try get an item to process off it. Remove the node from the list.
339652419Sjulian */
339770700Sjulianstatic void
3398186093Smavngthread(void *arg)
339952419Sjulian{
3400177953Smav	for (;;) {
3401177953Smav		node_p  node;
340252419Sjulian
3403177953Smav		/* Get node from the worklist. */
3404168049Swkoszek		NG_WORKLIST_LOCK();
3405186093Smav		while ((node = STAILQ_FIRST(&ng_worklist)) == NULL)
3406186093Smav			NG_WORKLIST_SLEEP();
3407178228Smav		STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
3408168049Swkoszek		NG_WORKLIST_UNLOCK();
3409193731Szec		CURVNET_SET(node->nd_vnet);
3410154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
3411154275Sglebius		    __func__, node->nd_ID, node);
341270700Sjulian		/*
341370700Sjulian		 * We have the node. We also take over the reference
341470700Sjulian		 * that the list had on it.
341570700Sjulian		 * Now process as much as you can, until it won't
341670700Sjulian		 * let you have another item off the queue.
341770700Sjulian		 * All this time, keep the reference
341870700Sjulian		 * that lets us be sure that the node still exists.
341970700Sjulian		 * Let the reference go at the last minute.
342070700Sjulian		 */
342170700Sjulian		for (;;) {
3422177953Smav			item_p item;
3423151238Sglebius			int rw;
3424151238Sglebius
3425168049Swkoszek			NG_QUEUE_LOCK(&node->nd_input_queue);
3426178228Smav			item = ng_dequeue(node, &rw);
342770700Sjulian			if (item == NULL) {
3428178228Smav				node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
3429168049Swkoszek				NG_QUEUE_UNLOCK(&node->nd_input_queue);
343070700Sjulian				break; /* go look for another node */
343170700Sjulian			} else {
3432168049Swkoszek				NG_QUEUE_UNLOCK(&node->nd_input_queue);
343374078Sjulian				NGI_GET_NODE(item, node); /* zaps stored node */
3434151238Sglebius				ng_apply_item(node, item, rw);
343574078Sjulian				NG_NODE_UNREF(node);
343670700Sjulian			}
343770700Sjulian		}
343873238Sjulian		NG_NODE_UNREF(node);
3439193731Szec		CURVNET_RESTORE();
344052419Sjulian	}
344170700Sjulian}
344269922Sjulian
344372979Sjulian/*
344472979Sjulian * XXX
344572979Sjulian * It's posible that a debugging NG_NODE_REF may need
344672979Sjulian * to be outside the mutex zone
344772979Sjulian */
344870700Sjulianstatic void
3449177953Smavng_worklist_add(node_p node)
345070700Sjulian{
3451148236Sglebius
3452148266Sglebius	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3453148236Sglebius
3454178228Smav	if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
345569922Sjulian		/*
345670700Sjulian		 * If we are not already on the work queue,
345770700Sjulian		 * then put us on.
345869922Sjulian		 */
3459178228Smav		node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
3460229003Sglebius		NG_NODE_REF(node); /* XXX safe in mutex? */
3461168049Swkoszek		NG_WORKLIST_LOCK();
3462178228Smav		STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
3463168049Swkoszek		NG_WORKLIST_UNLOCK();
3464154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
3465154275Sglebius		    node->nd_ID, node);
3466186093Smav		NG_WORKLIST_WAKEUP();
3467177953Smav	} else {
3468154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
3469154275Sglebius		    __func__, node->nd_ID, node);
3470177953Smav	}
347170700Sjulian}
347270700Sjulian
347370700Sjulian/***********************************************************************
347470700Sjulian* Externally useable functions to set up a queue item ready for sending
347570700Sjulian***********************************************************************/
347670700Sjulian
347770784Sjulian#ifdef	NETGRAPH_DEBUG
347870784Sjulian#define	ITEM_DEBUG_CHECKS						\
347970700Sjulian	do {								\
348071849Sjulian		if (NGI_NODE(item) ) {					\
348170700Sjulian			printf("item already has node");		\
3482174898Srwatson			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
348371849Sjulian			NGI_CLR_NODE(item);				\
348470700Sjulian		}							\
348571849Sjulian		if (NGI_HOOK(item) ) {					\
348670700Sjulian			printf("item already has hook");		\
3487174898Srwatson			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
348871849Sjulian			NGI_CLR_HOOK(item);				\
348970700Sjulian		}							\
349070700Sjulian	} while (0)
349170700Sjulian#else
349270784Sjulian#define ITEM_DEBUG_CHECKS
349370700Sjulian#endif
349470700Sjulian
349570700Sjulian/*
3496131374Sjulian * Put mbuf into the item.
349770700Sjulian * Hook and node references will be removed when the item is dequeued.
349870700Sjulian * (or equivalent)
349970700Sjulian * (XXX) Unsafe because no reference held by peer on remote node.
350070700Sjulian * remote node might go away in this timescale.
350170700Sjulian * We know the hooks can't go away because that would require getting
350270700Sjulian * a writer item on both nodes and we must have at least a  reader
3503151973Sglebius * here to be able to do this.
350470700Sjulian * Note that the hook loaded is the REMOTE hook.
350570700Sjulian *
350670700Sjulian * This is possibly in the critical path for new data.
350770700Sjulian */
350870700Sjulianitem_p
3509146281Sglebiusng_package_data(struct mbuf *m, int flags)
351070700Sjulian{
351170700Sjulian	item_p item;
351270700Sjulian
3513178259Smav	if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
3514176849Smav		NG_FREE_M(m);
3515176849Smav		return (NULL);
3516176849Smav	}
351770784Sjulian	ITEM_DEBUG_CHECKS;
3518178259Smav	item->el_flags |= NGQF_READER;
351970700Sjulian	NGI_M(item) = m;
352070700Sjulian	return (item);
352170700Sjulian}
352270700Sjulian
352370700Sjulian/*
352470700Sjulian * Allocate a queue item and put items into it..
352570700Sjulian * Evaluate the address as this will be needed to queue it and
352670700Sjulian * to work out what some of the fields should be.
352770700Sjulian * Hook and node references will be removed when the item is dequeued.
352870700Sjulian * (or equivalent)
352970700Sjulian */
353070700Sjulianitem_p
3531146281Sglebiusng_package_msg(struct ng_mesg *msg, int flags)
353270700Sjulian{
353370700Sjulian	item_p item;
353470700Sjulian
3535178259Smav	if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
353671849Sjulian		NG_FREE_MSG(msg);
353770700Sjulian		return (NULL);
353869922Sjulian	}
353970784Sjulian	ITEM_DEBUG_CHECKS;
3540149505Sglebius	/* Messages items count as writers unless explicitly exempted. */
3541149505Sglebius	if (msg->header.cmd & NGM_READONLY)
3542178259Smav		item->el_flags |= NGQF_READER;
3543149505Sglebius	else
3544178259Smav		item->el_flags |= NGQF_WRITER;
354570700Sjulian	/*
354670700Sjulian	 * Set the current lasthook into the queue item
354770700Sjulian	 */
354870700Sjulian	NGI_MSG(item) = msg;
3549102244Sarchie	NGI_RETADDR(item) = 0;
355070700Sjulian	return (item);
355170700Sjulian}
355269922Sjulian
355371849Sjulian#define SET_RETADDR(item, here, retaddr)				\
355471047Sjulian	do {	/* Data or fn items don't have retaddrs */		\
355571047Sjulian		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
355670700Sjulian			if (retaddr) {					\
355770700Sjulian				NGI_RETADDR(item) = retaddr;		\
355870700Sjulian			} else {					\
355970700Sjulian				/*					\
356070700Sjulian				 * The old return address should be ok.	\
356170700Sjulian				 * If there isn't one, use the address	\
356270700Sjulian				 * here.				\
356370700Sjulian				 */					\
356470700Sjulian				if (NGI_RETADDR(item) == 0) {		\
356570700Sjulian					NGI_RETADDR(item)		\
356670700Sjulian						= ng_node2ID(here);	\
356770700Sjulian				}					\
356870700Sjulian			}						\
356970700Sjulian		}							\
357070700Sjulian	} while (0)
357170700Sjulian
357270700Sjulianint
357370700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
357470700Sjulian{
357571849Sjulian	hook_p peer;
357671849Sjulian	node_p peernode;
357770784Sjulian	ITEM_DEBUG_CHECKS;
357870700Sjulian	/*
357970700Sjulian	 * Quick sanity check..
358070784Sjulian	 * Since a hook holds a reference on it's node, once we know
358170784Sjulian	 * that the peer is still connected (even if invalid,) we know
358270784Sjulian	 * that the peer node is present, though maybe invalid.
358370700Sjulian	 */
3584256550Smelifaro	TOPOLOGY_RLOCK();
3585229003Sglebius	if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) ||
3586178311Smav	    NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
3587178311Smav	    NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
358870700Sjulian		NG_FREE_ITEM(item);
358971047Sjulian		TRAP_ERROR();
3590256550Smelifaro		TOPOLOGY_RUNLOCK();
359173083Sjulian		return (ENETDOWN);
359252419Sjulian	}
359352419Sjulian
359470700Sjulian	/*
359570700Sjulian	 * Transfer our interest to the other (peer) end.
359670700Sjulian	 */
359771849Sjulian	NG_HOOK_REF(peer);
3598178311Smav	NG_NODE_REF(peernode);
359971849Sjulian	NGI_SET_HOOK(item, peer);
360071849Sjulian	NGI_SET_NODE(item, peernode);
360179706Sjulian	SET_RETADDR(item, here, retaddr);
3602219827Sglebius
3603256550Smelifaro	TOPOLOGY_RUNLOCK();
3604219827Sglebius
360570700Sjulian	return (0);
360670700Sjulian}
360752419Sjulian
360870700Sjulianint
3609227130Sfjoeng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr)
361070700Sjulian{
3611152451Sglebius	node_p	dest = NULL;
361270700Sjulian	hook_p	hook = NULL;
3613152451Sglebius	int	error;
361470700Sjulian
361570784Sjulian	ITEM_DEBUG_CHECKS;
361670700Sjulian	/*
361770700Sjulian	 * Note that ng_path2noderef increments the reference count
361870700Sjulian	 * on the node for us if it finds one. So we don't have to.
361970700Sjulian	 */
362070700Sjulian	error = ng_path2noderef(here, address, &dest, &hook);
362170700Sjulian	if (error) {
362270700Sjulian		NG_FREE_ITEM(item);
362370784Sjulian		return (error);
362452419Sjulian	}
362571849Sjulian	NGI_SET_NODE(item, dest);
3626219827Sglebius	if (hook)
362771849Sjulian		NGI_SET_HOOK(item, hook);
3628219827Sglebius
362971849Sjulian	SET_RETADDR(item, here, retaddr);
363070700Sjulian	return (0);
363170700Sjulian}
363252419Sjulian
363370700Sjulianint
363470700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
363570700Sjulian{
363670700Sjulian	node_p dest;
363770700Sjulian
363870784Sjulian	ITEM_DEBUG_CHECKS;
363970700Sjulian	/*
364070700Sjulian	 * Find the target node.
364170700Sjulian	 */
364270700Sjulian	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
364370700Sjulian	if (dest == NULL) {
364470700Sjulian		NG_FREE_ITEM(item);
364571047Sjulian		TRAP_ERROR();
364670700Sjulian		return(EINVAL);
364770700Sjulian	}
364870700Sjulian	/* Fill out the contents */
364971849Sjulian	NGI_SET_NODE(item, dest);
365071849Sjulian	NGI_CLR_HOOK(item);
365171849Sjulian	SET_RETADDR(item, here, retaddr);
365252419Sjulian	return (0);
365352419Sjulian}
365452419Sjulian
365552419Sjulian/*
365670700Sjulian * special case to send a message to self (e.g. destroy node)
365770700Sjulian * Possibly indicate an arrival hook too.
365870700Sjulian * Useful for removing that hook :-)
365952419Sjulian */
366070700Sjulianitem_p
366170700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
366252419Sjulian{
366370700Sjulian	item_p item;
366452419Sjulian
366570700Sjulian	/*
366670700Sjulian	 * Find the target node.
366770700Sjulian	 * If there is a HOOK argument, then use that in preference
366870700Sjulian	 * to the address.
366970700Sjulian	 */
3670178259Smav	if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
367171849Sjulian		NG_FREE_MSG(msg);
367270700Sjulian		return (NULL);
367352419Sjulian	}
367470700Sjulian
367570700Sjulian	/* Fill out the contents */
3676178259Smav	item->el_flags |= NGQF_WRITER;
367770784Sjulian	NG_NODE_REF(here);
367871849Sjulian	NGI_SET_NODE(item, here);
367971849Sjulian	if (hook) {
368070784Sjulian		NG_HOOK_REF(hook);
368171849Sjulian		NGI_SET_HOOK(item, hook);
368271849Sjulian	}
368370700Sjulian	NGI_MSG(item) = msg;
368470700Sjulian	NGI_RETADDR(item) = ng_node2ID(here);
368570700Sjulian	return (item);
368652419Sjulian}
368752419Sjulian
3688172806Smav/*
3689172806Smav * Send ng_item_fn function call to the specified node.
3690172806Smav */
3691172806Smav
3692146281Sglebiusint
3693173605Sglebiusng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
369471047Sjulian{
369571047Sjulian
3696173605Sglebius	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
369771047Sjulian}
369871047Sjulian
3699172806Smavint
3700173605Sglebiusng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
3701172806Smav	int flags)
3702172806Smav{
3703172806Smav	item_p item;
3704172806Smav
3705178259Smav	if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
3706172806Smav		return (ENOMEM);
3707172806Smav	}
3708178259Smav	item->el_flags |= NGQF_WRITER;
3709172806Smav	NG_NODE_REF(node); /* and one for the item */
3710172806Smav	NGI_SET_NODE(item, node);
3711172806Smav	if (hook) {
3712172806Smav		NG_HOOK_REF(hook);
3713172806Smav		NGI_SET_HOOK(item, hook);
3714172806Smav	}
3715173605Sglebius	NGI_FN(item) = fn;
3716172806Smav	NGI_ARG1(item) = arg1;
3717172806Smav	NGI_ARG2(item) = arg2;
3718172806Smav	return(ng_snd_item(item, flags));
3719172806Smav}
3720172806Smav
3721172806Smav/*
3722173605Sglebius * Send ng_item_fn2 function call to the specified node.
3723173605Sglebius *
3724173605Sglebius * If an optional pitem parameter is supplied, its apply
3725173605Sglebius * callback will be copied to the new item. If also NG_REUSE_ITEM
3726173605Sglebius * flag is set, no new item will be allocated, but pitem will
3727173605Sglebius * be used.
3728172806Smav */
3729172806Smavint
3730173605Sglebiusng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3731173605Sglebius	int arg2, int flags)
3732172806Smav{
3733172806Smav	item_p item;
3734172806Smav
3735173605Sglebius	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3736173605Sglebius	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3737172806Smav
3738173605Sglebius	/*
3739173605Sglebius	 * Allocate a new item if no supplied or
3740173605Sglebius	 * if we can't use supplied one.
3741173605Sglebius	 */
3742173605Sglebius	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
3743178259Smav		if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
3744173605Sglebius			return (ENOMEM);
3745178259Smav		if (pitem != NULL)
3746178259Smav			item->apply = pitem->apply;
3747176849Smav	} else {
3748178259Smav		if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
3749178259Smav			return (ENOMEM);
3750176849Smav	}
3751172806Smav
3752178259Smav	item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
3753172806Smav	NG_NODE_REF(node); /* and one for the item */
3754172806Smav	NGI_SET_NODE(item, node);
3755172806Smav	if (hook) {
3756172806Smav		NG_HOOK_REF(hook);
3757172806Smav		NGI_SET_HOOK(item, hook);
3758172806Smav	}
3759172806Smav	NGI_FN2(item) = fn;
3760172806Smav	NGI_ARG1(item) = arg1;
3761172806Smav	NGI_ARG2(item) = arg2;
3762172806Smav	return(ng_snd_item(item, flags));
3763172806Smav}
3764172806Smav
3765172806Smav/*
376691711Sjulian * Official timeout routines for Netgraph nodes.
376791711Sjulian */
376891711Sjulianstatic void
3769140852Sglebiusng_callout_trampoline(void *arg)
377091711Sjulian{
377191711Sjulian	item_p item = arg;
377291711Sjulian
3773193731Szec	CURVNET_SET(NGI_NODE(item)->nd_vnet);
377491711Sjulian	ng_snd_item(item, 0);
3775193731Szec	CURVNET_RESTORE();
377691711Sjulian}
377791711Sjulian
3778137138Sglebiusint
3779138268Sglebiusng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
378091711Sjulian    ng_item_fn *fn, void * arg1, int arg2)
378191711Sjulian{
3782149881Sglebius	item_p item, oitem;
378391711Sjulian
3784178259Smav	if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
3785137138Sglebius		return (ENOMEM);
3786137138Sglebius
3787178259Smav	item->el_flags |= NGQF_WRITER;
378891711Sjulian	NG_NODE_REF(node);		/* and one for the item */
378991711Sjulian	NGI_SET_NODE(item, node);
379091711Sjulian	if (hook) {
379191711Sjulian		NG_HOOK_REF(hook);
379291711Sjulian		NGI_SET_HOOK(item, hook);
379391711Sjulian	}
379491711Sjulian	NGI_FN(item) = fn;
379591711Sjulian	NGI_ARG1(item) = arg1;
379691711Sjulian	NGI_ARG2(item) = arg2;
3797149881Sglebius	oitem = c->c_arg;
3798149881Sglebius	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
3799149881Sglebius	    oitem != NULL)
3800149881Sglebius		NG_FREE_ITEM(oitem);
3801137138Sglebius	return (0);
380291711Sjulian}
380391711Sjulian
380491711Sjulian/* A special modified version of untimeout() */
3805152451Sglebiusint
3806138268Sglebiusng_uncallout(struct callout *c, node_p node)
380791711Sjulian{
380891711Sjulian	item_p item;
3809137138Sglebius	int rval;
3810149357Sglebius
3811149357Sglebius	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
3812149357Sglebius	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
3813149357Sglebius
3814137230Sglebius	rval = callout_stop(c);
3815137138Sglebius	item = c->c_arg;
3816137138Sglebius	/* Do an extra check */
3817140852Sglebius	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
3818333615Ssbruno	    (item != NULL) && (NGI_NODE(item) == node)) {
381991711Sjulian		/*
382091711Sjulian		 * We successfully removed it from the queue before it ran
3821152451Sglebius		 * So now we need to unreference everything that was
382291711Sjulian		 * given extra references. (NG_FREE_ITEM does this).
382391711Sjulian		 */
382491711Sjulian		NG_FREE_ITEM(item);
382591711Sjulian	}
3826149881Sglebius	c->c_arg = NULL;
3827137138Sglebius
3828310248Shselasky	/*
3829310248Shselasky	 * Callers only want to know if the callout was cancelled and
3830310248Shselasky	 * not draining or stopped.
3831310248Shselasky	 */
3832310248Shselasky	return (rval > 0);
383391711Sjulian}
383491711Sjulian
383570700Sjulian/*
383670700Sjulian * Set the address, if none given, give the node here.
383770700Sjulian */
383870700Sjulianvoid
383970700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
384070700Sjulian{
384170700Sjulian	if (retaddr) {
384270700Sjulian		NGI_RETADDR(item) = retaddr;
384370700Sjulian	} else {
384470700Sjulian		/*
384570700Sjulian		 * The old return address should be ok.
384670700Sjulian		 * If there isn't one, use the address here.
384770700Sjulian		 */
384870700Sjulian		NGI_RETADDR(item) = ng_node2ID(here);
384970700Sjulian	}
385070700Sjulian}
3851