Subversion Repositories shark

Rev

Rev 502 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
206 giacomo 1
/*
2
 * Project: S.Ha.R.K.
3
 *
4
 * Coordinators:
5
 *   Giorgio Buttazzo    <giorgio@sssup.it>
6
 *   Paolo Gai           <pj@gandalf.sssup.it>
7
 *
8
 * Authors     :
9
 *   Paolo Gai           <pj@gandalf.sssup.it>
10
 *   Massimiliano Giorgi <massy@gandalf.sssup.it>
11
 *   Luca Abeni          <luca@gandalf.sssup.it>
211 giacomo 12
 *   Giacomo Guidi       <giacomo@gandalf.sssup.it>
13
 *   (see the web pages for full authors list)
206 giacomo 14
 *
15
 * ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
16
 *
17
 * http://www.sssup.it
18
 * http://retis.sssup.it
19
 * http://shark.sssup.it
20
 */
21
 
22
/*
23
 * Copyright (C) 2000 Paolo Gai
24
 *
25
 * This program is free software; you can redistribute it and/or modify
26
 * it under the terms of the GNU General Public License as published by
27
 * the Free Software Foundation; either version 2 of the License, or
28
 * (at your option) any later version.
29
 *
30
 * This program is distributed in the hope that it will be useful,
31
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33
 * GNU General Public License for more details.
34
 *
35
 * You should have received a copy of the GNU General Public License
36
 * along with this program; if not, write to the Free Software
37
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
38
 *
39
 */
40
 
41
#include <modules/hardcbs.h>
42
#include <ll/stdio.h>
43
#include <ll/string.h>
44
#include <kernel/model.h>
45
#include <kernel/descr.h>
46
#include <kernel/var.h>
47
#include <kernel/func.h>
48
 
353 giacomo 49
#include <tracer.h>
50
 
206 giacomo 51
/*+ Status used in the level +*/
52
#define HCBS_IDLE          APER_STATUS_BASE   /*+ waiting the activation +*/
53
#define HCBS_ZOMBIE        APER_STATUS_BASE+1 /*+ waiting the period end +*/
54
 
55
/*+ task flags +*/
56
#define HCBS_SAVE_ARRIVALS 1
57
#define HCBS_APERIODIC     2
58
#define HCBS_SLEEP         4
59
#define HCBS_OVERLOAD      8
60
 
61
/*+ the level redefinition for the Total Bandwidth Server level +*/
62
typedef struct {
63
  level_des l;     /*+ the standard level descriptor          +*/
64
 
65
  /* The wcet are stored in the task descriptor, but we need
66
     an array for the deadlines. We can't use the timespec_priority
67
     field because it is used by the master level!!!...
68
     Notice that however the use of the timespec_priority field
69
     does not cause any problem...                     */
70
 
71
  struct timespec cbs_dline[MAX_PROC]; /*+ CBS deadlines      +*/
72
 
73
  TIME period[MAX_PROC]; /*+ CBS activation period            +*/
74
 
75
  struct timespec reactivation_time[MAX_PROC];
76
        /*+ the time at witch  the reactivation timer is post +*/
77
  int reactivation_timer[MAX_PROC];
78
                                   /*+ the recativation timer +*/
79
 
80
  int nact[MAX_PROC]; /*+ number of pending activations       +*/
81
 
82
  BYTE flag[MAX_PROC]; /*+ task flags                         +*/
83
 
84
  int flags;       /*+ the init flags...                      +*/
85
 
86
  bandwidth_t U;   /*+ the used bandwidth by the server       +*/
87
 
88
  LEVEL scheduling_level;
89
 
90
} HCBS_level_des;
91
 
92
static void HCBS_activation(HCBS_level_des *lev,
211 giacomo 93
                            PID p,
94
                            struct timespec *acttime)
206 giacomo 95
{
96
  JOB_TASK_MODEL job;
97
 
98
  /* we have to check if the deadline and the wcet are correct before
99
     activating a new task or an old task... */
100
 
101
  /* check 1: if the deadline is before than the actual scheduling time */
102
 
103
  /* check 2: if ( avail_time >= (cbs_dline - acttime)* (wcet/period) )
104
     (rule 7 in the CBS article!) */
105
  TIME t;
106
  struct timespec t2,t3;
107
 
108
  t = (lev->period[p] * proc_table[p].avail_time) / proc_table[p].wcet;
109
  t3.tv_sec = t / 1000000;
110
  t3.tv_nsec = (t % 1000000) * 1000;
111
 
112
  SUBTIMESPEC(&lev->cbs_dline[p], acttime, &t2);
113
 
114
  if (/* 1 */ TIMESPEC_A_LT_B(&lev->cbs_dline[p], acttime) ||
115
      /* 2 */ TIMESPEC_A_GT_B(&t3, &t2) ) {
211 giacomo 116
/*    if (TIMESPEC_A_LT_B(&lev->cbs_dline[p], acttime) )
117
      kern_printf("$");
118
    else
119
      kern_printf("(Ûdline%d.%d act%d.%d wcet%d per%d avail%dÛ)",
120
                  lev->cbs_dline[p].tv_sec,lev->cbs_dline[p].tv_nsec/1000,
121
                  acttime->tv_sec, acttime->tv_nsec/1000,
122
                  proc_table[p].wcet, lev->period[p], proc_table[p].avail_time);
123
*/  /* we modify the deadline ... */
206 giacomo 124
    TIMESPEC_ASSIGN(&lev->cbs_dline[p], acttime);
125
    ADDUSEC2TIMESPEC(lev->period[p], &lev->cbs_dline[p]);
126
 
127
    /* and the capacity */
128
    proc_table[p].avail_time = proc_table[p].wcet;
129
  }
130
 
131
  /* and, finally, we reinsert the task in the master level */
132
  job_task_default_model(job, lev->cbs_dline[p]);
133
  job_task_def_noexc(job);
134
  level_table[ lev->scheduling_level ]->
135
    private_insert(lev->scheduling_level, p, (TASK_MODEL *)&job);
211 giacomo 136
}
206 giacomo 137
 
211 giacomo 138
static void HCBS_reload(HCBS_level_des *lev, PID p)
139
{
140
 
141
  proc_table[p].avail_time += proc_table[p].wcet;
142
 
206 giacomo 143
}
144
 
145
static void HCBS_avail_time_check(HCBS_level_des *lev, PID p)
146
{
211 giacomo 147
  /* Only one time due to hard reservation */
148
  if (proc_table[p].avail_time < 0)
206 giacomo 149
    ADDUSEC2TIMESPEC(lev->period[p], &lev->cbs_dline[p]);
150
 
151
}
152
 
153
 
154
/* this is the periodic reactivation of the task... it is posted only
155
   if the task is a periodic task */
156
static void HCBS_timer_reactivate(void *par)
157
{
158
  PID p = (PID) par;
159
  HCBS_level_des *lev;
160
 
161
  lev = (HCBS_level_des *)level_table[proc_table[p].task_level];
162
 
163
  if (lev->flag[p] & HCBS_SLEEP && proc_table[p].status == HCBS_IDLE) {
164
    proc_table[p].status = SLEEP;
211 giacomo 165
    proc_table[p].avail_time = proc_table[p].wcet;
166
    NULL_TIMESPEC(&lev->cbs_dline[p]);
206 giacomo 167
    return;
168
  }
169
 
211 giacomo 170
  /* Hard reservation avail_time reload */
171
  HCBS_reload(lev, p);
172
 
206 giacomo 173
  if (proc_table[p].status == HCBS_IDLE) {
174
    /* the task has finished the current activation and must be
175
       reactivated */
176
    HCBS_activation(lev,p,&lev->reactivation_time[p]);
177
 
178
    event_need_reschedule();
179
  }
180
  else if (lev->flag[p] & HCBS_SAVE_ARRIVALS)
181
    /* the task has not completed the current activation, so we save
182
       the activation incrementing nact... */
183
    lev->nact[p]++;
184
 
213 giacomo 185
  if (!(lev->flag[p] & HCBS_APERIODIC)) {
186
    /* repost the event at the next period end... */
187
    ADDUSEC2TIMESPEC(lev->period[p], &lev->reactivation_time[p]);
188
    lev->reactivation_timer[p] = kern_event_post(&lev->reactivation_time[p],
189
                                                 HCBS_timer_reactivate,
190
                                                 (void *)p);
191
  }
206 giacomo 192
 
193
  /* tracer stuff */
502 giacomo 194
  TRACER_LOGEVENT(FTrace_EVT_task_timer,(unsigned short int)proc_table[p].context,(unsigned int)proc_table[p].task_level);  
206 giacomo 195
 
196
}
197
 
198
/*+ this function is called when a killed or ended task reach the
199
    period end +*/
200
static void HCBS_timer_zombie(void *par)
201
{
202
  PID p = (PID) par;
211 giacomo 203
  HCBS_level_des *lev;
204
 
205
  lev = (HCBS_level_des *)level_table[proc_table[p].task_level];
206
 
207
  /* we finally put the task in the ready queue */
206 giacomo 208
  proc_table[p].status = FREE;
209
  iq_insertfirst(p,&freedesc);
210
 
211
}
212
 
211 giacomo 213
 
206 giacomo 214
/* The on-line guarantee is enabled only if the appropriate flag is set... */
215
static int HCBS_public_guarantee(LEVEL l, bandwidth_t *freebandwidth)
216
{
217
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
218
 
219
  if (*freebandwidth >= lev->U) {
220
    *freebandwidth -= lev->U;
221
    return 1;
222
  }
223
  else
224
    return 0;
225
}
226
 
227
static int HCBS_public_create(LEVEL l, PID p, TASK_MODEL *m)
228
{
229
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
230
  SOFT_TASK_MODEL *soft;
231
 
232
  if (m->pclass != SOFT_PCLASS) return -1;
233
  if (m->level != 0 && m->level != l) return -1;
234
  soft = (SOFT_TASK_MODEL *)m;
235
  if (!(soft->met && soft->period)) return -1;
236
 
237
  soft = (SOFT_TASK_MODEL *)m;
238
 
239
  if (lev->flags & HCBS_ENABLE_GUARANTEE) {
240
    bandwidth_t b;
241
    b = (MAX_BANDWIDTH / soft->period) * soft->met;
242
 
243
    /* really update lev->U, checking an overflow... */
244
    if (MAX_BANDWIDTH - lev->U > b)
245
      lev->U += b;
246
    else
247
      return -1;
248
  }
249
 
250
  /* Enable wcet check */
251
  proc_table[p].avail_time = soft->met;
252
  proc_table[p].wcet       = soft->met;
253
  proc_table[p].control   |= CONTROL_CAP;
254
 
255
  lev->nact[p] = 0;
256
  lev->period[p] = soft->period;
257
  NULL_TIMESPEC(&lev->cbs_dline[p]);
258
 
259
  if (soft->periodicity == APERIODIC)
260
    lev->flag[p] = HCBS_APERIODIC;
261
  else
262
    lev->flag[p] = 0;
263
 
264
  if (soft->arrivals == SAVE_ARRIVALS)
265
    lev->flag[p] |= HCBS_SAVE_ARRIVALS;
266
 
267
  return 0; /* OK, also if the task cannot be guaranteed... */
268
}
269
 
270
static void HCBS_public_detach(LEVEL l, PID p)
271
{
272
  /* the CBS level doesn't introduce any dinamic allocated new field.
273
     we have only to decrement the allocated bandwidth */
274
 
275
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
276
 
277
  if (lev->flags & HCBS_ENABLE_GUARANTEE) {
278
    lev->U -= (MAX_BANDWIDTH / lev->period[p]) * proc_table[p].wcet;
279
  }
280
}
281
 
282
static int HCBS_public_eligible(LEVEL l, PID p)
283
{
284
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
285
  JOB_TASK_MODEL job;
286
 
287
  /* we have to check if the deadline and the wcet are correct...
288
     if the CBS level schedules in background with respect to others
289
     levels, there can be the case in witch a task is scheduled by
290
     schedule_time > CBS_deadline; in this case (not covered in the
291
     article because if there is only the standard scheduling policy
292
     this never apply) we reassign the deadline */
293
 
294
  if ( TIMESPEC_A_LT_B(&lev->cbs_dline[p], &schedule_time) ) {
295
    /* we kill the current activation */
296
    level_table[ lev->scheduling_level ]->
297
      private_extract(lev->scheduling_level, p);
298
 
299
    /* we modify the deadline ... */
300
    TIMESPEC_ASSIGN(&lev->cbs_dline[p], &schedule_time);
301
    ADDUSEC2TIMESPEC(lev->period[p], &lev->cbs_dline[p]);
302
 
303
    /* and the capacity */
304
    proc_table[p].avail_time = proc_table[p].wcet;
305
 
306
    /* and, finally, we reinsert the task in the master level */
307
    job_task_default_model(job, lev->cbs_dline[p]);
308
    job_task_def_noexc(job);
309
    level_table[ lev->scheduling_level ]->
310
      private_insert(lev->scheduling_level, p, (TASK_MODEL *)&job);
311
 
312
    return -1;
313
  }
211 giacomo 314
 
206 giacomo 315
  return 0;
316
}
317
 
318
static void HCBS_public_dispatch(LEVEL l, PID p, int nostop)
319
{
320
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
321
  level_table[ lev->scheduling_level ]->
322
    private_dispatch(lev->scheduling_level,p,nostop);
323
}
324
 
325
static void HCBS_public_epilogue(LEVEL l, PID p)
326
{
327
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
328
 
329
  /* check if the wcet is finished... */
330
  if ( proc_table[p].avail_time <= 0) {
331
 
211 giacomo 332
    /* Set the overload flag */
206 giacomo 333
    lev->flag[p] |= HCBS_OVERLOAD;
334
 
335
    /* we kill the current activation */
336
    level_table[ lev->scheduling_level ]->
337
      private_extract(lev->scheduling_level, p);
338
 
211 giacomo 339
    /* we modify the deadline according to rule 4 ... */
340
    HCBS_avail_time_check(lev, p);
341
 
206 giacomo 342
    /* Hard Reservation: The avail_time will be reloaded in the
343
       next reactivation event. The task is suspended */
344
    proc_table[p].status = HCBS_IDLE;
345
    if (lev->flag[p] & HCBS_APERIODIC) {
346
 
347
      ADDUSEC2TIMESPEC(lev->period[p], &lev->reactivation_time[p]);
348
      lev->reactivation_timer[p] = kern_event_post(&lev->reactivation_time[p],
349
                                                 HCBS_timer_reactivate,
350
                                                 (void *)p);
351
    }
211 giacomo 352
 
206 giacomo 353
  }
354
  else
355
    /* the task has been preempted. it returns into the ready queue by
356
       calling the guest_epilogue... */
357
    level_table[ lev->scheduling_level ]->
358
      private_epilogue(lev->scheduling_level,p);
359
}
360
 
657 anton 361
static void HCBS_public_activate(LEVEL l, PID p, struct timespec *t)
206 giacomo 362
{
363
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
364
 
365
  if (lev->flag[p] & HCBS_SLEEP) {
366
    lev->flag[p] &= ~HCBS_SLEEP;
367
    if (proc_table[p].status != SLEEP) return;
368
  }
369
 
370
  /* save activation (only if needed... */
371
  if (proc_table[p].status != SLEEP) {
372
    if (lev->flag[p] & HCBS_SAVE_ARRIVALS)
373
      lev->nact[p]++;
374
    return;
375
  }
376
 
657 anton 377
  HCBS_activation(lev, p, t);
206 giacomo 378
 
379
  /* Set the reactivation timer */
380
  if (!(lev->flag[p] & HCBS_APERIODIC))
381
  {
382
    /* we cannot use the deadline computed by CBS_activation because
383
       the deadline may be != from actual_time + period
384
       (if we call the task_activate after a task_sleep, and the
385
       deadline was postponed a lot...) */
657 anton 386
    TIMESPEC_ASSIGN(&lev->reactivation_time[p], t);
206 giacomo 387
    ADDUSEC2TIMESPEC(lev->period[p], &lev->reactivation_time[p]);
388
//    TIMESPEC_ASSIGN(&lev->reactivation_time[p], &lev->cbs_dline[p]);
389
    lev->reactivation_timer[p] = kern_event_post(&lev->reactivation_time[p],
390
                                                 HCBS_timer_reactivate,
391
                                                 (void *)p);
392
  }
211 giacomo 393
//  kern_printf("act : %d %d |",lev->cbs_dline[p].tv_nsec/1000,p);
206 giacomo 394
}
395
 
396
static void HCBS_public_unblock(LEVEL l, PID p)
397
{
398
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
399
  struct timespec acttime;
400
 
401
  kern_gettime(&acttime);
402
 
403
  HCBS_activation(lev,p,&acttime);
404
}
405
 
406
static void HCBS_public_block(LEVEL l, PID p)
407
{
408
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
409
 
410
  /* check if the wcet is finished... */
411
  HCBS_avail_time_check(lev, p);
412
 
413
  level_table[ lev->scheduling_level ]->
414
    private_extract(lev->scheduling_level,p);
415
}
416
 
417
static int HCBS_public_message(LEVEL l, PID p, void *m)
418
{
211 giacomo 419
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
206 giacomo 420
  HCBS_command_message *msg;
421
 
422
  switch((long)(m)) {
423
 
424
    case (long)(NULL):
425
 
211 giacomo 426
      /* check if the wcet is finished... */
427
      HCBS_avail_time_check(lev, p);
428
 
429
      if (lev->flag[p] & HCBS_OVERLOAD) {
430
        lev->nact[p]++;
206 giacomo 431
        lev->flag[p] &= ~HCBS_OVERLOAD;
432
      }
433
 
434
      if (lev->nact[p]) {
435
        /* continue!!!! */
436
        lev->nact[p]--;
437
        level_table[ lev->scheduling_level ]->
438
          private_epilogue(lev->scheduling_level,p);
439
      } else {
440
        level_table[ lev->scheduling_level ]->
441
          private_extract(lev->scheduling_level,p);
442
 
443
        if (lev->flag[p] & HCBS_APERIODIC)
444
          proc_table[p].status = SLEEP;
445
        else  /* the task is soft_periodic */
446
          proc_table[p].status = HCBS_IDLE;
447
      }
448
 
449
      jet_update_endcycle(); /* Update the Jet data... */
502 giacomo 450
      TRACER_LOGEVENT(FTrace_EVT_task_end_cycle,(unsigned short int)proc_table[p].context,(unsigned int)l);
206 giacomo 451
 
452
      break;
453
 
211 giacomo 454
    case 1:
206 giacomo 455
 
456
      lev->flag[p] |= HCBS_SLEEP;
502 giacomo 457
      TRACER_LOGEVENT(FTrace_EVT_task_disable,(unsigned short int)proc_table[p].context,(unsigned int)l);
206 giacomo 458
 
459
      break;
460
 
461
    /* Set/Get Met/Period */
462
    default:
463
 
464
      msg = (HCBS_command_message *)(m);
465
 
466
      switch (msg->command) {
467
        case HCBS_SET_PERIOD:
468
          lev->U -= (bandwidth_t)(MAX_BANDWIDTH / lev->period[p]
469
                * proc_table[p].wcet);
470
          lev->period[p] = msg->param;
471
          lev->U += (bandwidth_t)(MAX_BANDWIDTH / lev->period[p]
472
                * proc_table[p].wcet);
473
          break;
474
        case HCBS_GET_PERIOD:
475
          msg->param = lev->period[p];
476
          break;
477
        case HCBS_SET_MET:
478
          lev->U -= (bandwidth_t)(MAX_BANDWIDTH / lev->period[p]
479
                * proc_table[p].wcet);
480
          proc_table[p].wcet = msg->param;
481
          lev->U += (bandwidth_t)(MAX_BANDWIDTH / lev->period[p]
482
                * proc_table[p].wcet);
483
          break;
484
        case HCBS_GET_MET:
485
          msg->param = proc_table[p].wcet;
486
          break;
487
      }
488
 
489
  }
490
 
491
  return 0;
211 giacomo 492
 
206 giacomo 493
}
494
 
495
static void HCBS_public_end(LEVEL l, PID p)
496
{
497
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
498
 
499
  /* check if the wcet is finished... */
500
  HCBS_avail_time_check(lev, p);
501
 
502
  level_table[ lev->scheduling_level ]->
503
    private_extract(lev->scheduling_level,p);
504
 
211 giacomo 505
  /* and free the allocated bandwidth */
206 giacomo 506
  lev->U -= (MAX_BANDWIDTH/lev->period[p]) * proc_table[p].wcet;
507
 
508
  /* we delete the reactivation timer */
213 giacomo 509
  if (!(lev->flag[p] & HCBS_APERIODIC) ||
510
      (lev->flag[p] & HCBS_APERIODIC && lev->flag[p] & HCBS_OVERLOAD)) {
206 giacomo 511
    kern_event_delete(lev->reactivation_timer[p]);
512
    lev->reactivation_timer[p] = -1;
513
  }
514
 
515
  /* Finally, we post the zombie event. when the end period is reached,
516
     the task descriptor and banwidth are freed */
517
  proc_table[p].status = HCBS_ZOMBIE;
518
  lev->reactivation_timer[p] = kern_event_post(&lev->cbs_dline[p],
519
                                               HCBS_timer_zombie,
520
                                               (void *)p);
521
}
522
 
523
/* Registration functions */
524
 
525
/*+ Registration function:
526
    int flags                 the init flags ... see CBS.h +*/
527
LEVEL HCBS_register_level(int flags, LEVEL master)
528
{
529
  LEVEL l;            /* the level that we register */
530
  HCBS_level_des *lev;  /* for readableness only */
531
  PID i;              /* a counter */
532
 
533
  printk("HCBS_register_level\n");
534
 
535
  /* request an entry in the level_table */
536
  l = level_alloc_descriptor(sizeof(HCBS_level_des));
537
 
538
  lev = (HCBS_level_des *)level_table[l];
539
 
540
  /* fill the standard descriptor */
541
  if (flags & HCBS_ENABLE_GUARANTEE)
542
    lev->l.public_guarantee = HCBS_public_guarantee;
543
  else
544
    lev->l.public_guarantee = NULL;
545
  lev->l.public_create    = HCBS_public_create;
546
  lev->l.public_detach    = HCBS_public_detach;
547
  lev->l.public_end       = HCBS_public_end;
548
  lev->l.public_eligible  = HCBS_public_eligible;
549
  lev->l.public_dispatch  = HCBS_public_dispatch;
550
  lev->l.public_epilogue  = HCBS_public_epilogue;
551
  lev->l.public_activate  = HCBS_public_activate;
552
  lev->l.public_unblock   = HCBS_public_unblock;
553
  lev->l.public_block     = HCBS_public_block;
554
  lev->l.public_message   = HCBS_public_message;
555
 
556
  /* fill the CBS descriptor part */
557
  for (i=0; i<MAX_PROC; i++) {
558
     NULL_TIMESPEC(&lev->cbs_dline[i]);
559
     lev->period[i] = 0;
560
     NULL_TIMESPEC(&lev->reactivation_time[i]);
561
     lev->reactivation_timer[i] = -1;
562
     lev->nact[i] = 0;
563
     lev->flag[i] = 0;
564
  }
565
 
566
 
567
  lev->U = 0;
568
 
569
  lev->scheduling_level = master;
570
 
571
  lev->flags = flags;
572
 
573
  return l;
574
}
575
 
576
bandwidth_t HCBS_usedbandwidth(LEVEL l)
577
{
578
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
579
 
580
  return lev->U;
581
}
582
 
583
int HCBS_get_nact(LEVEL l, PID p)
584
{
585
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
586
 
587
  return lev->nact[p];
588
}
589