Subversion Repositories shark

Rev

Rev 385 | Go to most recent revision | 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
 
361
static void HCBS_public_activate(LEVEL l, PID p)
362
{
363
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
364
  struct timespec t;
365
 
366
  if (lev->flag[p] & HCBS_SLEEP) {
367
    lev->flag[p] &= ~HCBS_SLEEP;
368
    if (proc_table[p].status != SLEEP) return;
369
  }
370
 
371
  /* save activation (only if needed... */
372
  if (proc_table[p].status != SLEEP) {
373
    if (lev->flag[p] & HCBS_SAVE_ARRIVALS)
374
      lev->nact[p]++;
375
    return;
376
  }
377
 
378
  kern_gettime(&t);
379
 
380
  HCBS_activation(lev, p, &t);
381
 
382
  /* Set the reactivation timer */
383
  if (!(lev->flag[p] & HCBS_APERIODIC))
384
  {
385
    /* we cannot use the deadline computed by CBS_activation because
386
       the deadline may be != from actual_time + period
387
       (if we call the task_activate after a task_sleep, and the
388
       deadline was postponed a lot...) */
389
    TIMESPEC_ASSIGN(&lev->reactivation_time[p], &t);
390
    ADDUSEC2TIMESPEC(lev->period[p], &lev->reactivation_time[p]);
391
//    TIMESPEC_ASSIGN(&lev->reactivation_time[p], &lev->cbs_dline[p]);
392
    lev->reactivation_timer[p] = kern_event_post(&lev->reactivation_time[p],
393
                                                 HCBS_timer_reactivate,
394
                                                 (void *)p);
395
  }
211 giacomo 396
//  kern_printf("act : %d %d |",lev->cbs_dline[p].tv_nsec/1000,p);
206 giacomo 397
}
398
 
399
static void HCBS_public_unblock(LEVEL l, PID p)
400
{
401
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
402
  struct timespec acttime;
403
 
404
  kern_gettime(&acttime);
405
 
406
  HCBS_activation(lev,p,&acttime);
407
}
408
 
409
static void HCBS_public_block(LEVEL l, PID p)
410
{
411
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
412
 
413
  /* check if the wcet is finished... */
414
  HCBS_avail_time_check(lev, p);
415
 
416
  level_table[ lev->scheduling_level ]->
417
    private_extract(lev->scheduling_level,p);
418
}
419
 
420
static int HCBS_public_message(LEVEL l, PID p, void *m)
421
{
211 giacomo 422
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
206 giacomo 423
  HCBS_command_message *msg;
424
 
425
  switch((long)(m)) {
426
 
427
    case (long)(NULL):
428
 
211 giacomo 429
      /* check if the wcet is finished... */
430
      HCBS_avail_time_check(lev, p);
431
 
432
      if (lev->flag[p] & HCBS_OVERLOAD) {
433
        lev->nact[p]++;
206 giacomo 434
        lev->flag[p] &= ~HCBS_OVERLOAD;
435
      }
436
 
437
      if (lev->nact[p]) {
438
        /* continue!!!! */
439
        lev->nact[p]--;
440
        level_table[ lev->scheduling_level ]->
441
          private_epilogue(lev->scheduling_level,p);
442
      } else {
443
        level_table[ lev->scheduling_level ]->
444
          private_extract(lev->scheduling_level,p);
445
 
446
        if (lev->flag[p] & HCBS_APERIODIC)
447
          proc_table[p].status = SLEEP;
448
        else  /* the task is soft_periodic */
449
          proc_table[p].status = HCBS_IDLE;
450
      }
451
 
452
      jet_update_endcycle(); /* Update the Jet data... */
502 giacomo 453
      TRACER_LOGEVENT(FTrace_EVT_task_end_cycle,(unsigned short int)proc_table[p].context,(unsigned int)l);
206 giacomo 454
 
455
      break;
456
 
211 giacomo 457
    case 1:
206 giacomo 458
 
459
      lev->flag[p] |= HCBS_SLEEP;
502 giacomo 460
      TRACER_LOGEVENT(FTrace_EVT_task_disable,(unsigned short int)proc_table[p].context,(unsigned int)l);
206 giacomo 461
 
462
      break;
463
 
464
    /* Set/Get Met/Period */
465
    default:
466
 
467
      msg = (HCBS_command_message *)(m);
468
 
469
      switch (msg->command) {
470
        case HCBS_SET_PERIOD:
471
          lev->U -= (bandwidth_t)(MAX_BANDWIDTH / lev->period[p]
472
                * proc_table[p].wcet);
473
          lev->period[p] = msg->param;
474
          lev->U += (bandwidth_t)(MAX_BANDWIDTH / lev->period[p]
475
                * proc_table[p].wcet);
476
          break;
477
        case HCBS_GET_PERIOD:
478
          msg->param = lev->period[p];
479
          break;
480
        case HCBS_SET_MET:
481
          lev->U -= (bandwidth_t)(MAX_BANDWIDTH / lev->period[p]
482
                * proc_table[p].wcet);
483
          proc_table[p].wcet = msg->param;
484
          lev->U += (bandwidth_t)(MAX_BANDWIDTH / lev->period[p]
485
                * proc_table[p].wcet);
486
          break;
487
        case HCBS_GET_MET:
488
          msg->param = proc_table[p].wcet;
489
          break;
490
      }
491
 
492
  }
493
 
494
  return 0;
211 giacomo 495
 
206 giacomo 496
}
497
 
498
static void HCBS_public_end(LEVEL l, PID p)
499
{
500
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
501
 
502
  /* check if the wcet is finished... */
503
  HCBS_avail_time_check(lev, p);
504
 
505
  level_table[ lev->scheduling_level ]->
506
    private_extract(lev->scheduling_level,p);
507
 
211 giacomo 508
  /* and free the allocated bandwidth */
206 giacomo 509
  lev->U -= (MAX_BANDWIDTH/lev->period[p]) * proc_table[p].wcet;
510
 
511
  /* we delete the reactivation timer */
213 giacomo 512
  if (!(lev->flag[p] & HCBS_APERIODIC) ||
513
      (lev->flag[p] & HCBS_APERIODIC && lev->flag[p] & HCBS_OVERLOAD)) {
206 giacomo 514
    kern_event_delete(lev->reactivation_timer[p]);
515
    lev->reactivation_timer[p] = -1;
516
  }
517
 
518
  /* Finally, we post the zombie event. when the end period is reached,
519
     the task descriptor and banwidth are freed */
520
  proc_table[p].status = HCBS_ZOMBIE;
521
  lev->reactivation_timer[p] = kern_event_post(&lev->cbs_dline[p],
522
                                               HCBS_timer_zombie,
523
                                               (void *)p);
524
}
525
 
526
/* Registration functions */
527
 
528
/*+ Registration function:
529
    int flags                 the init flags ... see CBS.h +*/
530
LEVEL HCBS_register_level(int flags, LEVEL master)
531
{
532
  LEVEL l;            /* the level that we register */
533
  HCBS_level_des *lev;  /* for readableness only */
534
  PID i;              /* a counter */
535
 
536
  printk("HCBS_register_level\n");
537
 
538
  /* request an entry in the level_table */
539
  l = level_alloc_descriptor(sizeof(HCBS_level_des));
540
 
541
  lev = (HCBS_level_des *)level_table[l];
542
 
543
  /* fill the standard descriptor */
544
  if (flags & HCBS_ENABLE_GUARANTEE)
545
    lev->l.public_guarantee = HCBS_public_guarantee;
546
  else
547
    lev->l.public_guarantee = NULL;
548
  lev->l.public_create    = HCBS_public_create;
549
  lev->l.public_detach    = HCBS_public_detach;
550
  lev->l.public_end       = HCBS_public_end;
551
  lev->l.public_eligible  = HCBS_public_eligible;
552
  lev->l.public_dispatch  = HCBS_public_dispatch;
553
  lev->l.public_epilogue  = HCBS_public_epilogue;
554
  lev->l.public_activate  = HCBS_public_activate;
555
  lev->l.public_unblock   = HCBS_public_unblock;
556
  lev->l.public_block     = HCBS_public_block;
557
  lev->l.public_message   = HCBS_public_message;
558
 
559
  /* fill the CBS descriptor part */
560
  for (i=0; i<MAX_PROC; i++) {
561
     NULL_TIMESPEC(&lev->cbs_dline[i]);
562
     lev->period[i] = 0;
563
     NULL_TIMESPEC(&lev->reactivation_time[i]);
564
     lev->reactivation_timer[i] = -1;
565
     lev->nact[i] = 0;
566
     lev->flag[i] = 0;
567
  }
568
 
569
 
570
  lev->U = 0;
571
 
572
  lev->scheduling_level = master;
573
 
574
  lev->flags = flags;
575
 
576
  return l;
577
}
578
 
579
bandwidth_t HCBS_usedbandwidth(LEVEL l)
580
{
581
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
582
 
583
  return lev->U;
584
}
585
 
586
int HCBS_get_nact(LEVEL l, PID p)
587
{
588
  HCBS_level_des *lev = (HCBS_level_des *)(level_table[l]);
589
 
590
  return lev->nact[p];
591
}
592