]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netpfil/ipfw/test/main.c
Import CK as of commit b19ed4c6a56ec93215ab567ba18ba61bf1cfbac8
[FreeBSD/FreeBSD.git] / sys / netpfil / ipfw / test / main.c
1 /*
2  * $FreeBSD$
3  *
4  * Testing program for schedulers
5  *
6  * The framework include a simple controller which, at each
7  * iteration, decides whether we can enqueue and/or dequeue.
8  * Then the mainloop runs the required number of tests,
9  * keeping track of statistics.
10  */
11
12 // #define USE_BURST    // what is this for ?
13
14 #include "dn_test.h"
15
16 struct cfg_s {
17         int ac;
18         char * const *av;
19
20         const char *name;
21         int loops;
22         struct timeval time;
23
24         /* running counters */
25         uint32_t        _enqueue;
26         uint32_t        drop;
27         uint32_t        pending;
28         uint32_t        dequeue;
29
30         /* generator parameters */
31         int32_t th_min, th_max; /* thresholds for hysteresis; negative means per flow */
32 #ifdef USE_BURST
33         int maxburst;
34 #endif /* USE_BURST */
35         int lmin, lmax; /* packet len */
36         int flows;      /* number of flows */
37         int flowsets;   /* number of flowsets */
38         int wsum;       /* sum of weights of all flows */
39 #ifdef USE_CUR
40         int max_y;      /* max random number in the generation */
41         int cur_y
42         int cur_fs;     /* used in generation, between 0 and max_y - 1 */
43 #endif /* USE_CUR */
44         const char *fs_config; /* flowset config */
45         int can_dequeue;
46         int burst;      /* count of packets sent in a burst */
47         struct mbuf *tosend;    /* packet to send -- also flag to enqueue */
48
49         struct mbuf *freelist;
50
51         struct mbuf *head, *tail;       /* a simple tailq */
52
53         /* scheduler hooks */
54         int (*enq)(struct dn_sch_inst *, struct dn_queue *,
55                 struct mbuf *);
56         struct mbuf * (*deq)(struct dn_sch_inst *);
57         /* size of the three fields including sched-specific areas */
58         uint32_t schk_len;
59         uint32_t q_len; /* size of a queue including sched-fields */
60         uint32_t si_len; /* size of a sch_inst including sched-fields */
61         char *q;        /* array of flow queues */
62                 /* use a char* because size is variable */
63         /*
64          * The scheduler template (one) followd by schk_datalen bytes
65          * for scheduler-specific parameters, total size is schk_len
66          */
67         struct dn_schk *sched;
68         /*
69          * one scheduler instance, followed by si_datalen bytes
70          * for scheduler specific parameters of this instance,
71          * total size is si_len. si->sched points to sched
72          */
73         struct dn_sch_inst *si;
74         struct dn_fsk *fs;      /* array of flowsets */
75
76         /* generator state */
77         int state;      /* 0 = going up (enqueue), 1: going down (dequeue) */
78
79         /*
80          * We keep lists for each backlog level, and always serve
81          * the one with shortest backlog. llmask contains a bitmap
82          * of lists, and ll are the heads of the lists. The last
83          * entry (BACKLOG) contains all entries considered 'full'
84          * XXX to optimize things, entry i could contain queues with
85          * 2^{i-1}+1 .. 2^i entries.
86          */
87 #define BACKLOG 30 /* this many backlogged classes, we only need BACKLOG+1 */
88         uint64_t        llmask;
89         struct list_head ll[BACKLOG + 10];
90
91         double *q_wfi;  /* (byte) Worst-case Fair Index of the flows  */
92         double wfi;     /* (byte) Worst-case Fair Index of the system */
93 };
94
95 /* FI2Q and Q2FI converts from flow_id (i.e. queue index)
96  * to dn_queue and back. We cannot simply use pointer arithmetic
97  * because the queu has variable size, q_len
98  */
99 #define FI2Q(c, i)      ((struct dn_queue *)((c)->q + (c)->q_len * (i)))
100 #define Q2FI(c, q)      (((char *)(q) - (c)->q)/(c)->q_len)
101
102 int debug = 0;
103
104 struct dn_parms dn_cfg;
105
106 static void controller(struct cfg_s *c);
107
108 /* release a packet for a given flow_id.
109  * Put the mbuf in the freelist, and in case move the
110  * flow to the end of the bucket.
111  */
112 static int
113 drop(struct cfg_s *c, struct mbuf *m)
114 {
115         struct dn_queue *q;
116         int i;
117
118         c->drop++;
119         q = FI2Q(c, m->flow_id);
120         i = q->ni.length; // XXX or ffs...
121
122         ND("q %p id %d current length %d", q, m->flow_id, i);
123         if (i < BACKLOG) {
124                 struct list_head *h = &q->ni.h;
125                 c->llmask &= ~(1<<(i+1));
126                 c->llmask |= (1<<(i));
127                 list_del(h);
128                 list_add_tail(h, &c->ll[i]);
129         }
130         m->m_nextpkt = c->freelist;
131         c->freelist = m;
132         return 0;
133 }
134
135
136 /*
137  * dn_sch_inst does not have a queue, for the RR we
138  * allocate a mq right after si
139  */
140 static int
141 default_enqueue(struct dn_sch_inst *si, struct dn_queue *q, struct mbuf *m)
142 {
143         struct mq *mq = (struct mq *)si;
144
145         (void)q;
146         /* this is the default function if no scheduler is provided */
147         if (mq->head == NULL)
148                 mq->head = m;
149         else
150                 mq->tail->m_nextpkt = m;
151         mq->tail = m;
152         return 0; /* default - success */
153 }
154
155
156 static struct mbuf *
157 default_dequeue(struct dn_sch_inst *si)
158 {
159         struct mq *mq = (struct mq *)si;
160         struct mbuf *m;
161         /* this is the default function if no scheduler is provided */
162         if ((m = mq->head)) {
163                 m = mq->head;
164                 mq->head = m->m_nextpkt;
165                 m->m_nextpkt = NULL;
166         }
167         return m;
168 }
169
170 static void
171 gnet_stats_enq(struct cfg_s *c, struct mbuf *mb)
172 {
173         struct dn_sch_inst *si = c->si;
174         struct dn_queue *_q = FI2Q(c, mb->flow_id);
175
176         if (_q->ni.length == 1) {
177                 _q->ni.bytes = 0;
178                 _q->ni.sch_bytes = si->ni.bytes;
179         }
180 }
181
182 static void
183 gnet_stats_deq(struct cfg_s *c, struct mbuf *mb)
184 {
185         struct dn_sch_inst *si = c->si;
186         struct dn_queue *_q = FI2Q(c, mb->flow_id);
187         int len = mb->m_pkthdr.len;
188
189         _q->ni.bytes += len;
190         si->ni.bytes += len;
191
192         if (_q->ni.length == 0) {
193                 double bytes = (double)_q->ni.bytes;
194                 double sch_bytes = (double)si->ni.bytes - _q->ni.sch_bytes;
195                 double weight = (double)_q->fs->fs.par[0] / c->wsum;
196                 double wfi = sch_bytes * weight - bytes;
197
198                 if (c->q_wfi[mb->flow_id] < wfi)
199                         c->q_wfi[mb->flow_id] = wfi;
200         }
201 }
202
203 static int
204 mainloop(struct cfg_s *c)
205 {
206         int i;
207         struct mbuf *m;
208
209         for (i=0; i < c->loops; i++) {
210                 /* implement histeresis */
211                 controller(c);
212                 DX(3, "loop %d enq %d send %p rx %d",
213                         i, c->_enqueue, c->tosend, c->can_dequeue);
214                 if ( (m = c->tosend) ) {
215                         int ret;
216                         struct dn_queue *q = FI2Q(c, m->flow_id);
217                         c->_enqueue++;
218                         ret = c->enq(c->si, q, m);
219                         if (ret) {
220                                 drop(c, m);
221                                 D("loop %d enqueue fail", i );
222                                 /*
223                                  * XXX do not insist; rather, try dequeue
224                                  */
225                                 goto do_dequeue;
226                         } else {
227                                 ND("enqueue ok");
228                                 c->pending++;
229                                 gnet_stats_enq(c, m);
230                         }
231                 } else if (c->can_dequeue) {
232 do_dequeue:
233                         c->dequeue++;
234                         m = c->deq(c->si);
235                         if (m) {
236                                 c->pending--;
237                                 drop(c, m);
238                                 c->drop--;      /* compensate */
239                                 gnet_stats_deq(c, m);
240                         } else {
241                                 D("--- ouch, cannot operate on iteration %d, pending %d", i, c->pending);
242                                 break;
243                         }
244                 }
245         }
246         DX(1, "mainloop ends %d", i);
247         return 0;
248 }
249
250 int
251 dump(struct cfg_s *c)
252 {
253         int i;
254
255         for (i=0; i < c->flows; i++) {
256                 //struct dn_queue *q = FI2Q(c, i);
257                 ND(1, "queue %4d tot %10llu", i,
258                     (unsigned long long)q->ni.tot_bytes);
259         }
260         DX(1, "done %d loops\n", c->loops);
261         return 0;
262 }
263
264 /* interpret a number in human form */
265 static long
266 getnum(const char *s, char **next, const char *key)
267 {
268         char *end = NULL;
269         long l;
270
271         if (next)       /* default */
272                 *next = NULL;
273         if (s && *s) {
274                 DX(3, "token is <%s> %s", s, key ? key : "-");
275                 l = strtol(s, &end, 0);
276         } else {
277                 DX(3, "empty string");
278                 l = -1;
279         }
280         if (l < 0) {
281                 DX(2, "invalid %s for %s", s ? s : "NULL", (key ? key : "") );
282                 return 0;       // invalid 
283         }
284         if (!end || !*end)
285                 return l;
286         if (*end == 'n')
287                 l = -l; /* multiply by n */
288         else if (*end == 'K')
289                 l = l*1000;
290         else if (*end == 'M')
291                 l = l*1000000;
292         else if (*end == 'k')
293                 l = l*1024;
294         else if (*end == 'm')
295                 l = l*1024*1024;
296         else if (*end == 'w')
297                 ;
298         else {/* not recognized */
299                 D("suffix %s for %s, next %p", end, key, next);
300                 end--;
301         }
302         end++;
303         DX(3, "suffix now %s for %s, next %p", end, key, next);
304         if (next && *end) {
305                 DX(3, "setting next to %s for %s", end, key);
306                 *next = end;
307         }
308         return l;
309 }
310
311 /*
312  * flowsets are a comma-separated list of
313  *     weight:maxlen:flows
314  * indicating how many flows are hooked to that fs.
315  * Both weight and range can be min-max-steps.
316  * The first pass (fs != NULL) justs count the number of flowsets and flows,
317  * the second pass (fs == NULL) we complete the setup.
318  */
319 static void
320 parse_flowsets(struct cfg_s *c, const char *fs)
321 {
322         char *s, *cur, *next;
323         int n_flows = 0, n_fs = 0, wsum = 0;
324         int i, j;
325         struct dn_fs *prev = NULL;
326         int pass = (fs == NULL);
327
328         DX(3, "--- pass %d flows %d flowsets %d", pass, c->flows, c->flowsets);
329         if (fs != NULL) { /* first pass */
330                 if (c->fs_config)
331                         D("warning, overwriting fs %s with %s",
332                                 c->fs_config, fs);
333                 c->fs_config = fs;
334         }
335         s = c->fs_config ? strdup(c->fs_config) : NULL;
336         if (s == NULL) {
337                 if (pass == 0)
338                         D("no fsconfig");
339                 return;
340         }
341         for (next = s; (cur = strsep(&next, ","));) {
342                 char *p = NULL;
343                 int w, w_h, w_steps, wi;
344                 int len, len_h, l_steps, li;
345                 int flows;
346
347                 w = getnum(strsep(&cur, ":"), &p, "weight");
348                 if (w <= 0)
349                         w = 1;
350                 w_h = p ? getnum(p+1, &p, "weight_max") : w;
351                 w_steps = p ? getnum(p+1, &p, "w_steps") : (w_h == w ?1:2);
352                 len = getnum(strsep(&cur, ":"), &p, "len");
353                 if (len <= 0)
354                         len = 1000;
355                 len_h = p ? getnum(p+1, &p, "len_max") : len;
356                 l_steps = p ? getnum(p+1, &p, "l_steps") : (len_h == len ? 1 : 2);
357                 flows = getnum(strsep(&cur, ":"), NULL, "flows");
358                 if (flows == 0)
359                         flows = 1;
360                 DX(4, "weight %d..%d (%d) len %d..%d (%d) flows %d",
361                         w, w_h, w_steps, len, len_h, l_steps, flows);
362                 if (w == 0 || w_h < w || len == 0 || len_h < len ||
363                                 flows == 0) {
364                         DX(4,"wrong parameters %s", s);
365                         return;
366                 }
367                 n_flows += flows * w_steps * l_steps;
368                 for (i = 0; i < w_steps; i++) {
369                         wi = w + ((w_h - w)* i)/(w_steps == 1 ? 1 : (w_steps-1));
370                         for (j = 0; j < l_steps; j++, n_fs++) {
371                                 struct dn_fs *fs = &c->fs[n_fs].fs; // tentative
372                                 int x;
373
374                                 li = len + ((len_h - len)* j)/(l_steps == 1 ? 1 : (l_steps-1));
375                                 x = (wi*2048)/li;
376                                 DX(3, "----- fs %4d weight %4d lmax %4d X %4d flows %d",
377                                         n_fs, wi, li, x, flows);
378                                 if (pass == 0)
379                                         continue;
380                                 if (c->fs == NULL || c->flowsets <= n_fs) {
381                                         D("error in number of flowsets");
382                                         return;
383                                 }
384                                 wsum += wi * flows;
385                                 fs->par[0] = wi;
386                                 fs->par[1] = li;
387                                 fs->index = n_fs;
388                                 fs->n_flows = flows;
389                                 fs->cur = fs->first_flow = prev==NULL ? 0 : prev->next_flow;
390                                 fs->next_flow = fs->first_flow + fs->n_flows;
391                                 fs->y = x * flows;
392                                 fs->base_y = (prev == NULL) ? 0 : prev->next_y;
393                                 fs->next_y = fs->base_y + fs->y;
394                                 prev = fs;
395                         }
396                 }
397         }
398         c->flows = n_flows;
399         c->flowsets = n_fs;
400         c->wsum = wsum;
401         if (pass == 0)
402                 return;
403
404         /* now link all flows to their parent flowsets */
405         DX(1,"%d flows on %d flowsets", c->flows, c->flowsets);
406 #ifdef USE_CUR
407         c->max_y = prev ? prev->base_y + prev->y : 0;
408         DX(1,"%d flows on %d flowsets max_y %d", c->flows, c->flowsets, c->max_y);
409 #endif /* USE_CUR */
410         for (i=0; i < c->flowsets; i++) {
411                 struct dn_fs *fs = &c->fs[i].fs;
412                 DX(1, "fs %3d w %5d l %4d flow %5d .. %5d y %6d .. %6d",
413                         i, fs->par[0], fs->par[1],
414                         fs->first_flow, fs->next_flow,
415                         fs->base_y, fs->next_y);
416                 for (j = fs->first_flow; j < fs->next_flow; j++) {
417                         struct dn_queue *q = FI2Q(c, j);
418                         q->fs = &c->fs[i];
419                 }
420         }
421 }
422
423 /* available schedulers */
424 extern moduledata_t *_g_dn_fifo;
425 extern moduledata_t *_g_dn_wf2qp;
426 extern moduledata_t *_g_dn_rr;
427 extern moduledata_t *_g_dn_qfq;
428 #ifdef WITH_QFQP
429 extern moduledata_t *_g_dn_qfqp;
430 #endif
431 #ifdef WITH_KPS
432 extern moduledata_t *_g_dn_kps;
433 #endif
434
435 static int
436 init(struct cfg_s *c)
437 {
438         int i;
439         int ac = c->ac;
440         char * const *av = c->av;
441
442         c->si_len = sizeof(struct dn_sch_inst);
443         c->q_len = sizeof(struct dn_queue);
444         moduledata_t *mod = NULL;
445         struct dn_alg *p = NULL;
446
447         c->th_min = -1; /* 1 packet per flow */
448         c->th_max = -20;/* 20 packets per flow */
449         c->lmin = c->lmax = 1280;       /* packet len */
450         c->flows = 1;
451         c->flowsets = 1;
452         c->name = "null";
453         ac--; av++;
454         while (ac > 1) {
455                 if (!strcmp(*av, "-n")) {
456                         c->loops = getnum(av[1], NULL, av[0]);
457                 } else if (!strcmp(*av, "-d")) {
458                         debug = atoi(av[1]);
459                 } else if (!strcmp(*av, "-alg")) {
460                         if (!strcmp(av[1], "rr"))
461                                 mod = _g_dn_rr;
462                         else if (!strcmp(av[1], "wf2qp"))
463                                 mod = _g_dn_wf2qp;
464                         else if (!strcmp(av[1], "fifo"))
465                                 mod = _g_dn_fifo;
466                         else if (!strcmp(av[1], "qfq"))
467                                 mod = _g_dn_qfq;
468 #ifdef WITH_QFQP
469                         else if (!strcmp(av[1], "qfq+") ||
470                             !strcmp(av[1], "qfqp") )
471                                 mod = _g_dn_qfqp;
472 #endif
473 #ifdef WITH_KPS
474                         else if (!strcmp(av[1], "kps"))
475                                 mod = _g_dn_kps;
476 #endif
477                         else
478                                 mod = NULL;
479                         c->name = mod ? mod->name : "NULL";
480                         DX(3, "using scheduler %s", c->name);
481                 } else if (!strcmp(*av, "-len")) {
482                         c->lmin = getnum(av[1], NULL, av[0]);
483                         c->lmax = c->lmin;
484                         DX(3, "setting max to %d", c->th_max);
485 #ifdef USE_BURST
486                 } else if (!strcmp(*av, "-burst")) {
487                         c->maxburst = getnum(av[1], NULL, av[0]);
488                         DX(3, "setting max to %d", c->th_max);
489 #endif /* USE_BURST */
490                 } else if (!strcmp(*av, "-qmax")) {
491                         c->th_max = getnum(av[1], NULL, av[0]);
492                         DX(3, "setting max to %d", c->th_max);
493                 } else if (!strcmp(*av, "-qmin")) {
494                         c->th_min = getnum(av[1], NULL, av[0]);
495                         DX(3, "setting min to %d", c->th_min);
496                 } else if (!strcmp(*av, "-flows")) {
497                         c->flows = getnum(av[1], NULL, av[0]);
498                         DX(3, "setting flows to %d", c->flows);
499                 } else if (!strcmp(*av, "-flowsets")) {
500                         parse_flowsets(c, av[1]); /* first pass */
501                         DX(3, "setting flowsets to %d", c->flowsets);
502                 } else {
503                         D("option %s not recognised, ignore", *av);
504                 }
505                 ac -= 2; av += 2;
506         }
507 #ifdef USE_BURST
508         if (c->maxburst <= 0)
509                 c->maxburst = 1;
510 #endif /* USE_BURST */
511         if (c->loops <= 0)
512                 c->loops = 1;
513         if (c->flows <= 0)
514                 c->flows = 1;
515         if (c->flowsets <= 0)
516                 c->flowsets = 1;
517         if (c->lmin <= 0)
518                 c->lmin = 1;
519         if (c->lmax <= 0)
520                 c->lmax = 1;
521         /* multiply by N */
522         if (c->th_min < 0)
523                 c->th_min = c->flows * -c->th_min;
524         if (c->th_max < 0)
525                 c->th_max = c->flows * -c->th_max;
526         if (c->th_max <= c->th_min)
527                 c->th_max = c->th_min + 1;
528
529         /* now load parameters from the module */
530         if (mod) {
531                 p = mod->p;
532                 DX(3, "using module %s f %p p %p", mod->name, mod->f, mod->p);
533                 DX(3, "modname %s ty %d", p->name, p->type);
534                 // XXX check enq and deq not null
535                 c->enq = p->enqueue;
536                 c->deq = p->dequeue;
537                 c->si_len += p->si_datalen;
538                 c->q_len += p->q_datalen;
539                 c->schk_len += p->schk_datalen;
540         } else {
541                 /* make sure c->si has room for a queue */
542                 c->enq = default_enqueue;
543                 c->deq = default_dequeue;
544         }
545
546         /* allocate queues, flowsets and one scheduler */
547         D("using %d flows, %d flowsets", c->flows, c->flowsets);
548         D("q_len %d dn_fsk %d si %d sched %d",
549                 c->q_len, (int)sizeof(struct dn_fsk),
550                 c->si_len, c->schk_len);
551         c->sched = calloc(1, c->schk_len); /* one parent scheduler */
552         c->si = calloc(1, c->si_len); /* one scheduler instance */
553         c->fs = calloc(c->flowsets, sizeof(struct dn_fsk));
554         c->q = calloc(c->flows, c->q_len);      /* one queue per flow */
555         c->q_wfi = calloc(c->flows, sizeof(double)); /* stats, one per flow */
556         if (!c->sched || !c->si || !c->fs || !c->q || !c->q_wfi) {
557                 D("error allocating memory");
558                 exit(1);
559         }
560         c->si->sched = c->sched; /* link scheduler instance to template */
561         if (p) {
562                 /* run initialization code if needed */
563                 if (p->config)
564                         p->config(c->si->sched);
565                 if (p->new_sched)
566                         p->new_sched(c->si);
567         }
568         /* parse_flowsets links queues to their flowsets */
569         parse_flowsets(c, NULL); /* second pass */
570         /* complete the work calling new_fsk */
571         for (i = 0; i < c->flowsets; i++) {
572                 struct dn_fsk *fsk = &c->fs[i];
573                 if (fsk->fs.par[1] == 0)
574                         fsk->fs.par[1] = 1000;  /* default pkt len */
575                 fsk->sched = c->si->sched;
576                 if (p && p->new_fsk)
577                         p->new_fsk(fsk);
578         }
579         /* --- now the scheduler is initialized --- */
580
581         /*
582          * initialize the lists for the generator, and put
583          * all flows in the list for backlog = 0
584          */
585         for (i=0; i <= BACKLOG+5; i++)
586                 INIT_LIST_HEAD(&c->ll[i]);
587
588         for (i = 0; i < c->flows; i++) {
589                 struct dn_queue *q = FI2Q(c, i);
590                 if (q->fs == NULL)
591                         q->fs = &c->fs[0]; /* XXX */
592                 q->_si = c->si;
593                 if (p && p->new_queue)
594                         p->new_queue(q);
595                 INIT_LIST_HEAD(&q->ni.h);
596                 list_add_tail(&q->ni.h, &c->ll[0]);
597         }
598         c->llmask = 1; /* all flows are in the first list */
599         return 0;
600 }
601
602
603 int
604 main(int ac, char *av[])
605 {
606         struct cfg_s c;
607         double ll;
608         int i;
609         char msg[40];
610
611         bzero(&c, sizeof(c));
612         c.ac = ac;
613         c.av = av;
614         init(&c);
615         gettimeofday(&c.time, NULL);
616         D("th_min %d th_max %d", c.th_min, c.th_max);
617
618         mainloop(&c);
619         {
620                 struct timeval end;
621                 gettimeofday(&end, NULL);
622                 timersub(&end, &c.time, &c.time);
623         }
624         ll = c.time.tv_sec*1000000 + c.time.tv_usec;
625         ll *= 1000;     /* convert to nanoseconds */
626         ll /= c._enqueue;
627         sprintf(msg, "1::%d", c.flows);
628         for (i = 0; i < c.flows; i++) {
629                 if (c.wfi < c.q_wfi[i])
630                         c.wfi = c.q_wfi[i];
631         }
632         D("sched=%-12s\ttime=%d.%03d sec (%.0f nsec) enq %lu %lu deq\n"
633            "\twfi=%.02f\tflow=%-16s",
634            c.name, (int)c.time.tv_sec, (int)c.time.tv_usec / 1000, ll,
635            (unsigned long)c._enqueue, (unsigned long)c.dequeue, c.wfi,
636            c.fs_config ? c.fs_config : msg);
637         dump(&c);
638         DX(1, "done ac %d av %p", ac, av);
639         for (i=0; i < ac; i++)
640                 DX(1, "arg %d %s", i, av[i]);
641         return 0;
642 }
643
644 /*
645  * The controller decides whether in this iteration we should send
646  * (the packet is in c->tosend) and/or receive (flag c->can_dequeue)
647  */
648 static void
649 controller(struct cfg_s *c)
650 {
651         struct mbuf *m;
652         struct dn_fs *fs;
653         int flow_id;
654
655         /* hysteresis between max and min */
656         if (c->state == 0 && c->pending >= (uint32_t)c->th_max)
657                 c->state = 1;
658         else if (c->state == 1 && c->pending <= (uint32_t)c->th_min)
659                 c->state = 0;
660         ND(1, "state %d pending %2d", c->state, c->pending);
661         c->can_dequeue = c->state;
662         c->tosend = NULL;
663         if (c->can_dequeue)
664                 return;
665
666         /*
667          * locate the flow to use for enqueueing
668          * We take the queue with the lowest number of queued packets,
669          * generate a packet for it, and put the queue in the next highest.
670          */
671     if (1) {
672         int i;
673         struct dn_queue *q;
674         struct list_head *h;
675
676         i = ffs(c->llmask) - 1;
677         if (i < 0) {
678                 D("no candidate");
679                 c->can_dequeue = 1;
680                 return;
681         }
682         h = &c->ll[i];
683         ND(1, "backlog %d p %p prev %p next %p", i, h, h->prev, h->next);
684         q = list_first_entry(h, struct dn_queue, ni.h);
685         list_del(&q->ni.h);
686         flow_id = Q2FI(c, q);
687         DX(2, "extracted flow %p %d backlog %d", q, flow_id, i);
688         if (list_empty(h)) {
689                 ND(2, "backlog %d empty", i);
690                 c->llmask &= ~(1<<i);
691         }
692         ND(1, "before %d p %p prev %p next %p", i+1, h+1, h[1].prev, h[1].next);
693         list_add_tail(&q->ni.h, h+1);
694         ND(1, " after %d p %p prev %p next %p", i+1, h+1, h[1].prev, h[1].next);
695         if (i < BACKLOG) {
696                 ND(2, "backlog %d full", i+1);
697                 c->llmask |= 1<<(1+i);
698         }
699         fs = &q->fs->fs;
700         fs->cur = flow_id;
701 #ifdef USE_CUR
702         c->cur_fs = q->fs - c->fs;
703     } else {
704         /* XXX this does not work ? */
705         /* now decide whom to send the packet, and the length */
706         /* lookup in the flow table */
707         if (c->cur_y >= c->max_y) {     /* handle wraparound */
708                 c->cur_y = 0;
709                 c->cur_fs = 0;
710         }
711         fs = &c->fs[c->cur_fs].fs;
712         flow_id = fs->cur++;
713         if (fs->cur >= fs->next_flow)
714                 fs->cur = fs->first_flow;
715         c->cur_y++;
716         if (c->cur_y >= fs->next_y)
717                 c->cur_fs++;
718 #endif /* USE_CUR */
719     }
720
721         /* construct a packet */
722         if (c->freelist) {
723                 m = c->tosend = c->freelist;
724                 c->freelist = c->freelist->m_nextpkt;
725         } else {
726                 m = c->tosend = calloc(1, sizeof(struct mbuf));
727         }
728         if (m == NULL)
729                 return;
730
731         //m->cfg = c;
732         m->m_nextpkt = NULL;
733         m->m_pkthdr.len = fs->par[1]; // XXX maxlen
734         m->flow_id = flow_id;
735
736         ND(2,"y %6d flow %5d fs %3d weight %4d len %4d",
737                 c->cur_y, m->flow_id, c->cur_fs,
738                 fs->par[0], m->m_pkthdr.len);
739
740 }