-dummynet_io(struct mbuf **m0, int dir, struct ip_fw_args *fwa)
-{
- struct mbuf *m = *m0, *head = NULL, *tail = NULL;
- struct dn_pkt_tag *pkt;
- struct m_tag *mtag;
- struct dn_flow_set *fs = NULL;
- struct dn_pipe *pipe;
- uint64_t len = m->m_pkthdr.len;
- struct dn_flow_queue *q = NULL;
- int is_pipe = fwa->rule.info & IPFW_IS_PIPE;
-
- KASSERT(m->m_nextpkt == NULL,
- ("dummynet_io: mbuf queue passed to dummynet"));
-
- DUMMYNET_LOCK();
- io_pkt++;
- /*
- * This is a dummynet rule, so we expect an O_PIPE or O_QUEUE rule.
- */
- if (is_pipe) {
- pipe = locate_pipe(fwa->rule.info & IPFW_INFO_MASK);
- if (pipe != NULL)
- fs = &(pipe->fs);
- } else
- fs = locate_flowset(fwa->rule.info & IPFW_INFO_MASK);
-
- if (fs == NULL)
- goto dropit; /* This queue/pipe does not exist! */
- pipe = fs->pipe;
- if (pipe == NULL) { /* Must be a queue, try find a matching pipe. */
- pipe = locate_pipe(fs->parent_nr);
- if (pipe != NULL)
- fs->pipe = pipe;
- else {
- printf("dummynet: no pipe %d for queue %d, drop pkt\n",
- fs->parent_nr, fs->fs_nr);
- goto dropit;
- }
- }
- q = find_queue(fs, &(fwa->f_id));
- if (q == NULL)
- goto dropit; /* Cannot allocate queue. */
-
- /* Update statistics, then check reasons to drop pkt. */
- q->tot_bytes += len;
- q->tot_pkts++;
- if (fs->plr && random() < fs->plr)
- goto dropit; /* Random pkt drop. */
- if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
- if (q->len_bytes > fs->qsize)
- goto dropit; /* Queue size overflow. */
- } else {
- if (q->len >= fs->qsize)
- goto dropit; /* Queue count overflow. */
- }
- if (fs->flags_fs & DN_IS_RED && red_drops(fs, q, len))
- goto dropit;
-
- /* XXX expensive to zero, see if we can remove it. */
- mtag = m_tag_get(PACKET_TAG_DUMMYNET,
- sizeof(struct dn_pkt_tag), M_NOWAIT | M_ZERO);
- if (mtag == NULL)
- goto dropit; /* Cannot allocate packet header. */
- m_tag_prepend(m, mtag); /* Attach to mbuf chain. */
-
- pkt = (struct dn_pkt_tag *)(mtag + 1);
- /*
- * Ok, i can handle the pkt now...
- * Build and enqueue packet + parameters.
- */
- pkt->rule = fwa->rule;
- pkt->rule.info &= IPFW_ONEPASS; /* only keep this info */
- pkt->dn_dir = dir;
- pkt->ifp = fwa->oif;
-
- if (q->head == NULL)
- q->head = m;
- else
- q->tail->m_nextpkt = m;
- q->tail = m;
- q->len++;
- q->len_bytes += len;
-
- if (q->head != m) /* Flow was not idle, we are done. */
- goto done;
-
- if (is_pipe) { /* Fixed rate queues. */
- if (q->idle_time < curr_time) {
- /* Calculate available burst size. */
- q->numbytes +=
- (curr_time - q->idle_time - 1) * pipe->bandwidth;
- if (q->numbytes > pipe->burst)
- q->numbytes = pipe->burst;
- if (io_fast)
- q->numbytes += pipe->bandwidth;
- }
- } else { /* WF2Q. */
- if (pipe->idle_time < curr_time &&
- pipe->scheduler_heap.elements == 0 &&
- pipe->not_eligible_heap.elements == 0) {
- /* Calculate available burst size. */
- pipe->numbytes +=
- (curr_time - pipe->idle_time - 1) * pipe->bandwidth;
- if (pipe->numbytes > 0 && pipe->numbytes > pipe->burst)
- pipe->numbytes = pipe->burst;
- if (io_fast)
- pipe->numbytes += pipe->bandwidth;
- }
- pipe->idle_time = curr_time;
- }
- /* Necessary for both: fixed rate & WF2Q queues. */
- q->idle_time = curr_time;
-
- /*
- * If we reach this point the flow was previously idle, so we need
- * to schedule it. This involves different actions for fixed-rate or
- * WF2Q queues.
- */
- if (is_pipe) {
- /* Fixed-rate queue: just insert into the ready_heap. */
- dn_key t = 0;
-
- if (pipe->bandwidth) {
- q->extra_bits = compute_extra_bits(m, pipe);
- t = set_ticks(m, q, pipe);
- }
- q->sched_time = curr_time;
- if (t == 0) /* Must process it now. */
- ready_event(q, &head, &tail);
- else
- heap_insert(&ready_heap, curr_time + t , q);
- } else {
- /*
- * WF2Q. First, compute start time S: if the flow was
- * idle (S = F + 1) set S to the virtual time V for the
- * controlling pipe, and update the sum of weights for the pipe;
- * otherwise, remove flow from idle_heap and set S to max(F,V).
- * Second, compute finish time F = S + len / weight.
- * Third, if pipe was idle, update V = max(S, V).
- * Fourth, count one more backlogged flow.
- */
- if (DN_KEY_GT(q->S, q->F)) { /* Means timestamps are invalid. */
- q->S = pipe->V;
- pipe->sum += fs->weight; /* Add weight of new queue. */
- } else {
- heap_extract(&(pipe->idle_heap), q);
- q->S = MAX64(q->F, pipe->V);
- }
- q->F = q->S + div64(len << MY_M, fs->weight);
-
- if (pipe->not_eligible_heap.elements == 0 &&
- pipe->scheduler_heap.elements == 0)
- pipe->V = MAX64(q->S, pipe->V);
- fs->backlogged++;
- /*
- * Look at eligibility. A flow is not eligibile if S>V (when
- * this happens, it means that there is some other flow already
- * scheduled for the same pipe, so the scheduler_heap cannot be
- * empty). If the flow is not eligible we just store it in the
- * not_eligible_heap. Otherwise, we store in the scheduler_heap
- * and possibly invoke ready_event_wfq() right now if there is
- * leftover credit.
- * Note that for all flows in scheduler_heap (SCH), S_i <= V,
- * and for all flows in not_eligible_heap (NEH), S_i > V.
- * So when we need to compute max(V, min(S_i)) forall i in
- * SCH+NEH, we only need to look into NEH.
- */
- if (DN_KEY_GT(q->S, pipe->V)) { /* Not eligible. */
- if (pipe->scheduler_heap.elements == 0)
- printf("dummynet: ++ ouch! not eligible but empty scheduler!\n");
- heap_insert(&(pipe->not_eligible_heap), q->S, q);
- } else {
- heap_insert(&(pipe->scheduler_heap), q->F, q);
- if (pipe->numbytes >= 0) { /* Pipe is idle. */
- if (pipe->scheduler_heap.elements != 1)
- printf("dummynet: OUCH! pipe should have been idle!\n");
- DPRINTF(("dummynet: waking up pipe %d at %d\n",
- pipe->pipe_nr, (int)(q->F >> MY_M)));
- pipe->sched_time = curr_time;
- ready_event_wfq(pipe, &head, &tail);
- }
- }