diff --git a/sys/netgraph/netgraph.h b/sys/netgraph/netgraph.h index b98f80eaabf2..396bef3c4f6d 100644 --- a/sys/netgraph/netgraph.h +++ b/sys/netgraph/netgraph.h @@ -106,6 +106,7 @@ struct ng_hook { #define HK_INVALID 0x0001 /* don't trust it! */ #define HK_QUEUE 0x0002 /* queue for later delivery */ #define HK_FORCE_WRITER 0x0004 /* Incoming data queued as a writer */ +#define HK_DEAD 0x0008 /* This is the dead hook.. don't free */ /* * Public Methods for hook @@ -1009,7 +1010,8 @@ item_p ng_package_data(struct mbuf *m, meta_p meta); item_p ng_package_msg(struct ng_mesg *msg); item_p ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg); void ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr); -int ng_rmnode_self(node_p here); +int ng_rmhook_self(hook_p hook); /* if a node wants to kill a hook */ +int ng_rmnode_self(node_p here); /* if a node wants to suicide */ int ng_snd_item(item_p item, int queue); /* diff --git a/sys/netgraph/ng_base.c b/sys/netgraph/ng_base.c index 54d6e07014fb..1d80233fe826 100644 --- a/sys/netgraph/ng_base.c +++ b/sys/netgraph/ng_base.c @@ -81,7 +81,72 @@ static void ng_dumpnodes(void); static void ng_dumphooks(void); #endif /* NETGRAPH_DEBUG */ +/* + * DEAD versions of the structures. + * In order to avoid races, it is sometimes neccesary to point + * at SOMETHING even though theoretically, the current entity is + * INVALID. Use these to avoid these races. + */ +struct ng_type ng_deadtype = { + NG_ABI_VERSION, + "dead", + NULL, /* modevent */ + NULL, /* constructor */ + NULL, /* rcvmsg */ + NULL, /* shutdown */ + NULL, /* newhook */ + NULL, /* findhook */ + NULL, /* connect */ + NULL, /* rcvdata */ + NULL, /* disconnect */ + NULL, /* cmdlist */ +}; +struct ng_node ng_deadnode = { + "dead", + &ng_deadtype, + NG_INVALID, + 1, /* refs */ + 0, /* numhooks */ + NULL, /* private */ + 0, /* ID */ + LIST_HEAD_INITIALIZER(ng_deadnode.hooks), + {}, /* all_nodes list entry */ + {}, /* id hashtable list entry */ + {}, /* workqueue entry */ + { 0, + {}, /* should never use! (should hang) */ + NULL, + &ng_deadnode.nd_input_queue.queue, + &ng_deadnode + }, +#ifdef NETGRAPH_DEBUG + ND_MAGIC, + __FILE__, + __LINE__, + {NULL} +#endif /* NETGRAPH_DEBUG */ +}; + +struct ng_hook ng_deadhook = { + "dead", + NULL, /* private */ + HK_INVALID | HK_DEAD, + 1, /* refs always >= 1 */ + &ng_deadhook, /* Peer is self */ + &ng_deadnode, /* attached to deadnode */ + {}, /* hooks list */ +#ifdef NETGRAPH_DEBUG + HK_MAGIC, + __FILE__, + __LINE__, + {NULL} +#endif /* NETGRAPH_DEBUG */ +}; + +/* + * END DEAD STRUCTURES + */ /* List nodes with unallocated work */ static TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist); static struct mtx ng_worklist_mtx; @@ -500,7 +565,7 @@ ng_make_node(const char *typename, node_p *nodepp) /* * Node has no constructor. We cannot ask for one * to be made. It must be brought into existance by - * some external agency. The external acency should + * some external agency. The external agency should * call ng_make_node_common() directly to get the * netgraph part initialised. */ @@ -904,12 +969,12 @@ ng_add_hook(node_p node, const char *name, hook_p *hookp) NG_NODE_REF(node); /* each hook counts as a reference */ /* Check if the node type code has something to say about it */ - if (node->nd_type->newhook != NULL) - if ((error = (*node->nd_type->newhook)(node, hook, name)) != 0) { + if (node->nd_type->newhook != NULL) { + if ((error = (*node->nd_type->newhook)(node, hook, name))) { NG_HOOK_UNREF(hook); /* this frees the hook */ return (error); } - + } /* * The 'type' agrees so far, so go ahead and link it in. * We'll ask again later when we actually connect the hooks. @@ -1207,6 +1272,35 @@ ng_rmnode_self(node_p here) return (ng_snd_item(item, 0)); } +#define NG_INTERNAL_RMHOOK 0x123456 +int +ng_rmhook_self(hook_p hook) +{ + item_p item; + struct ng_mesg *msg; + node_p node = NG_HOOK_NODE(hook); + + NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NG_INTERNAL_RMHOOK, 0, M_NOWAIT); + /* + * Try get a queue item to send it with. + * Hopefully since it has a reserve, we can get one. + * If we can't we are screwed anyhow. + * Increase the chances by flushing our queue first. + * We may free an item, (if we were the hog). + * Work in progress is allowed to complete. + * We also pretty much ensure that we come straight + * back in to do the shutdown. It may be a good idea + * to hold a reference actually to stop it from all + * going up in smoke. + */ + item = ng_package_msg_self(node, hook, msg); + if (item == NULL) { /* couldn't allocate item. Freed msg */ + /* try again after flushing our queue */ + panic("Couldn't allocate item to remove hook"); + } + return (ng_snd_item(item, 0)); +} + /*********************************************************************** * Parse and verify a string of the form: * @@ -2231,6 +2325,9 @@ ng_generic_msg(node_p here, item_p item, hook_p lasthook) ng_destroy_hook(hook); break; } + case NG_INTERNAL_RMHOOK: + ng_destroy_hook(lasthook); + break; case NGM_NODEINFO: { struct nodeinfo *ni; diff --git a/sys/netgraph/ng_pppoe.c b/sys/netgraph/ng_pppoe.c index 77e96688cd8e..8e44423c74e1 100644 --- a/sys/netgraph/ng_pppoe.c +++ b/sys/netgraph/ng_pppoe.c @@ -1167,7 +1167,7 @@ AAA /* send message to creator */ /* close hook */ if (sendhook) { - ng_destroy_hook(sendhook); + ng_rmhook_self(sendhook); } break; default: @@ -1511,7 +1511,7 @@ AAA case PPPOE_PRIMED: case PPPOE_SOFFER: /* a timeout on these says "give up" */ - ng_destroy_hook(hook); + ng_rmhook_self(hook); break; default: /* timeouts have no meaning in other states */