Changeset View
Standalone View
sys/netgraph/ng_source.c
Show First 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | |||||
#define mtod_off(m,off,t) ((t)(mtod((m),caddr_t)+(off))) | #define mtod_off(m,off,t) ((t)(mtod((m),caddr_t)+(off))) | ||||
/* Per node info */ | /* Per node info */ | ||||
struct privdata { | struct privdata { | ||||
node_p node; | node_p node; | ||||
hook_p input; | hook_p input; | ||||
hook_p output; | hook_p output; | ||||
struct ng_source_stats stats; | struct ng_source_stats stats; | ||||
struct ifqueue snd_queue; /* packets to send */ | struct mbufq snd_queue; /* packets to send */ | ||||
struct mbuf *last_packet; /* last pkt in queue */ | struct mbuf *last_packet; /* last pkt in queue */ | ||||
struct ifnet *output_ifp; | struct ifnet *output_ifp; | ||||
struct callout intr_ch; | struct callout intr_ch; | ||||
uint64_t packets; /* packets to send */ | uint64_t packets; /* packets to send */ | ||||
uint32_t queueOctets; | uint32_t queueOctets; | ||||
struct ng_source_embed_info embed_timestamp; | struct ng_source_embed_info embed_timestamp; | ||||
struct ng_source_embed_cnt_info embed_counter[NG_SOURCE_COUNTERS]; | struct ng_source_embed_cnt_info embed_counter[NG_SOURCE_COUNTERS]; | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 182 Lines • ▼ Show 20 Lines | |||||
ng_source_constructor(node_p node) | ng_source_constructor(node_p node) | ||||
{ | { | ||||
sc_p sc; | sc_p sc; | ||||
sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO); | sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO); | ||||
NG_NODE_SET_PRIVATE(node, sc); | NG_NODE_SET_PRIVATE(node, sc); | ||||
sc->node = node; | sc->node = node; | ||||
sc->snd_queue.ifq_maxlen = 2048; | mbufq_init(&sc->snd_queue, 2048); | ||||
donner: Is there a common idiom for that?
Do I need to initialize the mbufq? | |||||
ng_callout_init(&sc->intr_ch); | ng_callout_init(&sc->intr_ch); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Add a hook | * Add a hook | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | case NGM_SOURCE_GETCLR_STATS: | ||||
if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { | if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { | ||||
NG_MKRESPONSE(resp, msg, | NG_MKRESPONSE(resp, msg, | ||||
sizeof(*stats), M_NOWAIT); | sizeof(*stats), M_NOWAIT); | ||||
if (resp == NULL) { | if (resp == NULL) { | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto done; | goto done; | ||||
} | } | ||||
sc->stats.queueOctets = sc->queueOctets; | sc->stats.queueOctets = sc->queueOctets; | ||||
sc->stats.queueFrames = sc->snd_queue.ifq_len; | sc->stats.queueFrames = mbufq_len(&sc->snd_queue); | ||||
if ((sc->node->nd_flags & NG_SOURCE_ACTIVE) | if ((sc->node->nd_flags & NG_SOURCE_ACTIVE) | ||||
&& !timevalisset(&sc->stats.endTime)) { | && !timevalisset(&sc->stats.endTime)) { | ||||
getmicrotime(&sc->stats.elapsedTime); | getmicrotime(&sc->stats.elapsedTime); | ||||
timevalsub(&sc->stats.elapsedTime, | timevalsub(&sc->stats.elapsedTime, | ||||
&sc->stats.startTime); | &sc->stats.startTime); | ||||
} | } | ||||
stats = (struct ng_source_stats *)resp->data; | stats = (struct ng_source_stats *)resp->data; | ||||
bcopy(&sc->stats, stats, sizeof(* stats)); | bcopy(&sc->stats, stats, sizeof(* stats)); | ||||
▲ Show 20 Lines • Show All 177 Lines • ▼ Show 20 Lines | ng_source_rcvdata(hook_p hook, item_p item) | ||||
if (hook == sc->output) { | if (hook == sc->output) { | ||||
/* discard */ | /* discard */ | ||||
NG_FREE_M(m); | NG_FREE_M(m); | ||||
return (error); | return (error); | ||||
} | } | ||||
KASSERT(hook == sc->input, ("%s: no hook!", __func__)); | KASSERT(hook == sc->input, ("%s: no hook!", __func__)); | ||||
/* Enqueue packet if the queue isn't full. */ | /* Enqueue packet if the queue isn't full. */ | ||||
if (_IF_QFULL(&sc->snd_queue)) { | error = mbufq_enqueue(&sc->snd_queue, m); | ||||
if (error) { | |||||
NG_FREE_M(m); | NG_FREE_M(m); | ||||
return (ENOBUFS); | return (error); | ||||
} | } | ||||
_IF_ENQUEUE(&sc->snd_queue, m); | |||||
sc->queueOctets += m->m_pkthdr.len; | sc->queueOctets += m->m_pkthdr.len; | ||||
sc->last_packet = m; | sc->last_packet = m; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Shutdown processing | * Shutdown processing | ||||
▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | |||||
* Clear out the data we've queued | * Clear out the data we've queued | ||||
*/ | */ | ||||
static void | static void | ||||
ng_source_clr_data (sc_p sc) | ng_source_clr_data (sc_p sc) | ||||
{ | { | ||||
struct mbuf *m; | struct mbuf *m; | ||||
for (;;) { | for (;;) { | ||||
_IF_DEQUEUE(&sc->snd_queue, m); | m = mbufq_dequeue(&sc->snd_queue); | ||||
if (m == NULL) | if (m == NULL) | ||||
break; | break; | ||||
NG_FREE_M(m); | NG_FREE_M(m); | ||||
} | } | ||||
sc->queueOctets = 0; | sc->queueOctets = 0; | ||||
sc->last_packet = NULL; | sc->last_packet = NULL; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | if (sc->packets == 0 || sc->output == NULL | ||||
ng_source_stop(sc); | ng_source_stop(sc); | ||||
return; | return; | ||||
} | } | ||||
if (sc->output_ifp != NULL) { | if (sc->output_ifp != NULL) { | ||||
ifq = (struct ifqueue *)&sc->output_ifp->if_snd; | ifq = (struct ifqueue *)&sc->output_ifp->if_snd; | ||||
packets = ifq->ifq_maxlen - ifq->ifq_len; | packets = ifq->ifq_maxlen - ifq->ifq_len; | ||||
} else | } else | ||||
packets = sc->snd_queue.ifq_len; | packets = mbufq_len(&sc->snd_queue); | ||||
if (sc->stats.maxPps != 0) { | if (sc->stats.maxPps != 0) { | ||||
struct timeval now, elapsed; | struct timeval now, elapsed; | ||||
uint64_t usec; | uint64_t usec; | ||||
int maxpkt; | int maxpkt; | ||||
getmicrotime(&now); | getmicrotime(&now); | ||||
elapsed = now; | elapsed = now; | ||||
Show All 27 Lines | ng_source_send(sc_p sc, int tosend, int *sent_p) | ||||
KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE, | KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE, | ||||
("%s: inactive node", __func__)); | ("%s: inactive node", __func__)); | ||||
if ((uint64_t)tosend > sc->packets) | if ((uint64_t)tosend > sc->packets) | ||||
tosend = sc->packets; | tosend = sc->packets; | ||||
/* Go through the queue sending packets one by one. */ | /* Go through the queue sending packets one by one. */ | ||||
for (sent = 0; error == 0 && sent < tosend; ++sent) { | for (sent = 0; error == 0 && sent < tosend; ++sent) { | ||||
_IF_DEQUEUE(&sc->snd_queue, m); | m = mbufq_dequeue(&sc->snd_queue); | ||||
if (m == NULL) | if (m == NULL) | ||||
break; | break; | ||||
/* Duplicate and modify the packet. */ | /* Duplicate and modify the packet. */ | ||||
error = ng_source_dup_mod(sc, m, &m2); | error = ng_source_dup_mod(sc, m, &m2); | ||||
if (error) { | if (error) { | ||||
if (error == ENOBUFS) | if (error == ENOBUFS) | ||||
_IF_PREPEND(&sc->snd_queue, m); | mbufq_prepend(&sc->snd_queue, m); | ||||
else | else | ||||
_IF_ENQUEUE(&sc->snd_queue, m); | (void)mbufq_enqueue(&sc->snd_queue, m); | ||||
break; | break; | ||||
} | } | ||||
/* Re-enqueue the original packet for us. */ | /* | ||||
_IF_ENQUEUE(&sc->snd_queue, m); | * Re-enqueue the original packet for us. The queue | ||||
* has a free slot, because we dequeued the packet | |||||
* above and this callout function runs under WRITER | |||||
* lock. | |||||
*/ | |||||
error = mbufq_enqueue(&sc->snd_queue, m); | |||||
KASSERT(error == 0, ("%s: re-enqueue packet failed", __func__)); | |||||
Done Inline ActionsCan't this hit the limit if another thread is doing ng_source_rcvdata() at the same time? kp: Can't this hit the limit if another thread is doing ng_source_rcvdata() at the same time? | |||||
Done Inline ActionsUsually the node is used in the mode "learn" or "send" exclusively. It's a debugging tool. You are right, in principle there is the opportunity that new learned packets are inserted to the queue after the send routine here dequeued the packet. But it's only possible to add a single packet (because the queue is full), so the send function will try to re-add a single packet to a full queue but failed. So we end up with a modified list of packets in the queue, because existing and newly learned packets mix. But this is expected behavior. Only in the case of mbufq_prepend the mbufq logic will not check the limits, so the queue can go up unlimited. This requires memory pressure (ENOBUFS). In short: I do not see a problem here. donner: Usually the node is used in the mode "learn" or "send" exclusively. It's a debugging tool.
You… | |||||
Done Inline ActionsOkay. I wonder if it's worth adding a KASSERT() here, especially as we'd leak the mbuf if the enqueue fails. kp: Okay. I wonder if it's worth adding a KASSERT() here, especially as we'd leak the mbuf if the… | |||||
Done Inline ActionsThat's a serious point. the semantics of mbufq_enqueue differs from _IF_ENQUEUE in this aspect, so the case needs to be handled explicitly. donner: That's a serious point. the semantics of mbufq_enqueue differs from _IF_ENQUEUE in this aspect… | |||||
Done Inline ActionsForget it. This is a callout, which is always executed under a WRITER lock. So there is no interference possible. I'll document it. donner: Forget it. This is a callout, which is always executed under a WRITER lock. So there is no… | |||||
Done Inline ActionsThe KASSERT is an excellent way of documenting that it's impossible (and verifying assumption) :) kp: The KASSERT is an excellent way of documenting that it's impossible (and verifying assumption)… | |||||
sc->stats.outFrames++; | sc->stats.outFrames++; | ||||
sc->stats.outOctets += m2->m_pkthdr.len; | sc->stats.outOctets += m2->m_pkthdr.len; | ||||
NG_SEND_DATA_ONLY(error, sc->output, m2); | NG_SEND_DATA_ONLY(error, sc->output, m2); | ||||
if (error) | if (error) | ||||
break; | break; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 112 Lines • Show Last 20 Lines |
Is there a common idiom for that?
Do I need to initialize the mbufq?