diff options
author | glebius <glebius@FreeBSD.org> | 2005-07-05 17:35:20 +0000 |
---|---|---|
committer | glebius <glebius@FreeBSD.org> | 2005-07-05 17:35:20 +0000 |
commit | fcbdfd0eb4f509af7f2a382daaf2c52ce49704c8 (patch) | |
tree | 7c65a2a332dab7cec59b3eb4bdcbf403ceaac133 | |
parent | 9c552f7c01059c885fb0604e8fc082ce51f3228a (diff) | |
download | FreeBSD-src-fcbdfd0eb4f509af7f2a382daaf2c52ce49704c8.zip FreeBSD-src-fcbdfd0eb4f509af7f2a382daaf2c52ce49704c8.tar.gz |
In the splnet times, netgraph was functional and synchronous. Nowadays,
an item may be queued and processed later. While this is OK for mbufs,
this is a problem for control messages.
In the framework:
- Add optional callback function pointer to an item. When item gets
applied the callback is executed from ng_apply_item().
- Add new flag NG_PROGRESS. If this flag is supplied, then return
EINPROGRESS instead of 0 in case if item failed to deliver
synchronously and was queued.
- Honor NG_PROGRESS in ng_snd_item().
In ng_socket:
- When userland sends control message add callback to the item.
- If ng_snd_item() returns EINPROGRESS, then sleep.
This change fixes possible races in ngctl(8) scripts.
Reviewed by: julian
Approved by: re (scottl)
-rw-r--r-- | sys/netgraph/netgraph.h | 8 | ||||
-rw-r--r-- | sys/netgraph/ng_base.c | 30 | ||||
-rw-r--r-- | sys/netgraph/ng_socket.c | 45 | ||||
-rw-r--r-- | sys/netgraph/ng_socketvar.h | 2 |
4 files changed, 82 insertions, 3 deletions
diff --git a/sys/netgraph/netgraph.h b/sys/netgraph/netgraph.h index a963fc8..4304ced 100644 --- a/sys/netgraph/netgraph.h +++ b/sys/netgraph/netgraph.h @@ -580,6 +580,7 @@ _ng_node_foreach_hook(node_p node, ng_fn_eachhook *fn, void *arg, * */ typedef void ng_item_fn(node_p node, hook_p hook, void *arg1, int arg2); +typedef void ng_apply_t(void *context, int error); struct ng_item { u_long el_flags; item_p el_next; @@ -597,6 +598,12 @@ struct ng_item { int fn_arg2; } fn; } body; + /* + * Optional callback called when item is being applied, + * and its context. + */ + ng_apply_t *apply; + void *context; #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ char *lastfile; int lastline; @@ -1084,6 +1091,7 @@ int ng_callout(struct callout *c, node_p node, hook_p hook, int ticks, #define NG_NOFLAGS 0x00000000 /* no special options */ #define NG_QUEUE 0x00000001 /* enqueue item, don't dispatch */ #define NG_WAITOK 0x00000002 /* use M_WAITOK, etc. */ +#define NG_PROGRESS 0x00000004 /* return EINPROGRESS if queued */ /* * prototypes the user should DEFINITELY not use directly diff --git a/sys/netgraph/ng_base.c b/sys/netgraph/ng_base.c index 00650eec..5a9f8d9 100644 --- a/sys/netgraph/ng_base.c +++ b/sys/netgraph/ng_base.c @@ -2211,7 +2211,11 @@ ng_snd_item(item_p item, int flags) ng_setisr(node); } mtx_unlock_spin(&(ngq->q_mtx)); - return (0); + + if (flags & NG_PROGRESS) + return (EINPROGRESS); + else + return (0); } /* * Take a queue item and a node and see if we can apply the item to @@ -2237,7 +2241,10 @@ ng_snd_item(item_p item, int flags) * have been queued in thises cases. */ if (item == NULL) { - return (0); + if (flags & NG_PROGRESS) + return (EINPROGRESS); + else + return (0); } #ifdef NETGRAPH_DEBUG @@ -2333,11 +2340,25 @@ ng_apply_item(node_p node, item_p item) int error = 0; ng_rcvdata_t *rcvdata; ng_rcvmsg_t *rcvmsg; + ng_apply_t *apply = NULL; + void *context = NULL; NGI_GET_HOOK(item, hook); /* clears stored hook */ #ifdef NETGRAPH_DEBUG _ngi_check(item, __FILE__, __LINE__); #endif + + /* + * If item has apply callback, store it. Clear callback + * immediately, two avoid another call in case if + * item would be reused by destination node. + */ + if (item->apply != NULL) { + apply = item->apply; + context = item->context; + item->apply = NULL; + } + switch (item->el_flags & NGQF_TYPE) { case NGQF_DATA: /* @@ -2453,6 +2474,11 @@ ng_apply_item(node_p node, item_p item) } else { ng_leave_write(&node->nd_input_queue); } + + /* Apply callback. */ + if (apply != NULL) + (*apply)(context, error); + return (error); } diff --git a/sys/netgraph/ng_socket.c b/sys/netgraph/ng_socket.c index c016955..63be343 100644 --- a/sys/netgraph/ng_socket.c +++ b/sys/netgraph/ng_socket.c @@ -131,6 +131,7 @@ static int ng_bind(struct sockaddr *nam, struct ngpcb *pcbp); static int ngs_mod_event(module_t mod, int event, void *data); static int ship_msg(struct ngpcb *pcbp, struct ng_mesg *msg, struct sockaddr_ng *addr); +static void ng_socket_item_applied(void *context, int error); /* Netgraph type descriptor */ static struct ng_type typestruct = { @@ -208,6 +209,7 @@ ngc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct thread *td) { struct ngpcb *const pcbp = sotongpcb(so); + struct ngsock *const priv = NG_NODE_PRIVATE(pcbp->sockdata->node); struct sockaddr_ng *const sap = (struct sockaddr_ng *) addr; struct ng_mesg *msg; struct mbuf *m0; @@ -332,7 +334,29 @@ ngc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, item->el_dest->nd_type->name); #endif SAVE_LINE(item); - error = ng_snd_item(item, NG_NOFLAGS); + /* + * We do not want to return from syscall until the item + * is processed by destination node. We register callback + * on the item, which will update priv->error when item + * was applied. + * If ng_snd_item() has queued item, we sleep until + * callback wakes us up. + */ + item->apply = ng_socket_item_applied; + item->context = priv; + priv->error = -1; + + error = ng_snd_item(item, NG_PROGRESS); + + if (error == EINPROGRESS) { + mtx_lock(&priv->mtx); + if (priv->error == -1) + msleep(priv, &priv->mtx, 0, "ngsock", 0); + mtx_unlock(&priv->mtx); + KASSERT(priv->error != -1, + ("ng_socket: priv->error wasn't updated")); + error = priv->error; + } release: if (path != NULL) @@ -553,6 +577,8 @@ ng_attach_cntl(struct socket *so) } NG_NODE_SET_PRIVATE(privdata->node, privdata); + mtx_init(&privdata->mtx, "ng_socket", NULL, MTX_DEF); + /* Link the pcb and the node private data */ privdata->ctlsock = pcbp; pcbp->sockdata = privdata; @@ -813,6 +839,10 @@ ship_msg(struct ngpcb *pcbp, struct ng_mesg *msg, struct sockaddr_ng *addr) return (0); } +/*************************************************************** + Netgraph node +***************************************************************/ + /* * You can only create new nodes from the socket end of things. */ @@ -1020,10 +1050,23 @@ ngs_shutdown(node_p node) } NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_UNREF(node); + mtx_destroy(&priv->mtx); FREE(priv, M_NETGRAPH_SOCK); return (0); } +static void +ng_socket_item_applied(void *context, int error) +{ + struct ngsock *const priv = (struct ngsock *)context; + + mtx_lock(&priv->mtx); + priv->error = error; + wakeup(priv); + mtx_unlock(&priv->mtx); + +} + static int dummy_disconnect(struct socket *so) { diff --git a/sys/netgraph/ng_socketvar.h b/sys/netgraph/ng_socketvar.h index a539bb5..3cf8103 100644 --- a/sys/netgraph/ng_socketvar.h +++ b/sys/netgraph/ng_socketvar.h @@ -59,6 +59,8 @@ struct ngsock { struct ngpcb *ctlsock; /* optional control socket */ int flags; int refs; + struct mtx mtx; /* mtx to wait on */ + int error; /* place to store error */ }; #define NGS_FLAG_NOLINGER 1 /* close with last hook */ |