utcb_watch.c

Go to the documentation of this file.
00001 /*!
00002  * \file   cpu_reserve/lib/src/utcb_watch.c
00003  * \brief  Timeslice watching using the UTCB Ring-buffer of scheduling events
00004  *
00005  * \date   01/12/2005
00006  * \author Jork Loeser <jork.loeser@inf.tu-dresden.de>
00007  *
00008  */
00009 /* (c) 2005 Technische Universitaet Dresden
00010  * This file is part of DROPS, which is distributed under the terms of the
00011  * GNU General Public License 2. Please see the COPYING file for details.
00012  */
00013 #include <stdlib.h>
00014 #include <stdio.h>
00015 #include <string.h>
00016 #include <l4/sys/ipc.h>
00017 #include <l4/sys/types.h>
00018 #include <l4/cpu_reserve/utcb_stat.h>
00019 #include <l4/cpu_reserve/utcb_watch.h>
00020 #include <l4/env/errno.h>
00021 #include <l4/rt_mon/histogram.h>
00022 #include <l4/log/l4log.h>
00023 #include <l4/util/macros.h>
00024 #include <l4/util/util.h>
00025 #include <l4/thread/thread.h>
00026 
00027 static void watch_thread(void*arg);
00028 
00029 /*!\brief List-element to manage utcbs/threads */
00030 typedef struct utcb_list_t{
00031     l4_threadid_t thread;       /* thread id */
00032     l4cpu_reserve_utcb_t *utcb; /* utcb of that thread */
00033     int ts_count;               /* max. number of timeslices to watch */
00034     rt_mon_histogram_t **hists; /* histogramms attached to the thread */
00035     unsigned long period;       /* period number, more a debugging aid */
00036     unsigned long long release; /* release time of current period */
00037     int ts_num;                 /* last known id of current period */
00038     char name[L4CPU_RESERVE_UTCB_NAME_LEN]; /* name of the thread-watch */
00039     struct utcb_list_t *next;   /* for list usage */
00040 } utcb_list_t;
00041 
00042 /*!\brief Cmd-type for IPC-communication with watch thread */
00043 typedef enum watch_cmd_val_t {
00044     WATCH_CMD_INV,
00045     WATCH_CMD_ADD,
00046     WATCH_CMD_DEL,
00047 } watch_cmd_t;
00048 
00049 /*!\brief argument type for IPC-communication with watch thread */
00050 typedef struct{
00051     l4_threadid_t thread;
00052     l4cpu_reserve_utcb_t *utcb;
00053     char name[L4CPU_RESERVE_UTCB_NAME_LEN]; /* name of the thread-watch */
00054     unsigned maxtime;
00055     int ts_count;
00056 } watch_arg_t;
00057 
00058 //! the utcbs/threads are kept in this list
00059 static utcb_list_t *utcb_list;
00060 
00061 //! thread-id of watcher thread
00062 static l4_threadid_t watch_thread_id;
00063 
00064 /****************************************************************************
00065  *
00066  * User-interface implementation
00067  *
00068  ***************************************************************************/
00069 
00070 int l4cpu_reserve_utcb_watch_add(l4_threadid_t thread,
00071                                  l4cpu_reserve_utcb_t *utcb,
00072                                  const char*name,
00073                                  unsigned maxtime,
00074                                  int ts_count){
00075     l4_umword_t dummy;
00076     l4_msgdope_t result;
00077     int err, ans;
00078     watch_arg_t arg;
00079 
00080     arg.thread = thread;
00081     arg.utcb = utcb;
00082     strncpy(arg.name, name, L4CPU_RESERVE_UTCB_NAME_LEN);
00083     arg.name[L4CPU_RESERVE_UTCB_NAME_LEN-1]=0;
00084     arg.maxtime = maxtime;
00085     arg.ts_count = ts_count;
00086 
00087     if((err=l4_ipc_call(watch_thread_id, L4_IPC_SHORT_MSG,
00088                         WATCH_CMD_ADD,
00089                         (l4_umword_t)&arg,
00090                         L4_IPC_SHORT_MSG, (l4_umword_t *)&ans, &dummy,
00091                         L4_IPC_NEVER, &result))!=0) return err;
00092     return ans;
00093 }
00094 
00095 int l4cpu_reserve_utcb_watch_del(const l4cpu_reserve_utcb_t *utcb){
00096     l4_umword_t dummy;
00097     l4_msgdope_t result;
00098     int err, ans;
00099 
00100     if((err=l4_ipc_call(watch_thread_id, L4_IPC_SHORT_MSG,
00101                         WATCH_CMD_DEL,
00102                         (l4_umword_t)utcb,
00103                         L4_IPC_SHORT_MSG, (l4_umword_t *)&ans, &dummy,
00104                         L4_IPC_NEVER, &result))!=0) return err;
00105     return ans;
00106 }
00107 
00108 int l4cpu_reserve_utcb_watch_init(int poll_interval){
00109     l4thread_t w;
00110 
00111     if((w=l4thread_create_named(watch_thread, ".watcher",
00112                                 (void*)poll_interval,
00113                                 L4THREAD_CREATE_ASYNC))<0){
00114         l4env_perror("create watcher", -w);
00115         return w;
00116     }
00117     watch_thread_id = l4thread_l4_id(w);
00118     return 0;
00119 }
00120 
00121 /****************************************************************************
00122  *
00123  * Internal implementation at the watcher thread
00124  *
00125  ***************************************************************************/
00126 
00127 /*!\brief Actually add a new thread to the list of watched threads
00128  */
00129 static int watch_add_utcb_do(watch_arg_t*arg){
00130     int err=0, i;
00131     utcb_list_t *l;
00132 
00133     if(arg->ts_count==0) return -L4_EINVAL;
00134     if((l=calloc(sizeof(*l),1))==0) return -L4_ENOMEM;
00135 
00136     l->thread = arg->thread;
00137     l->ts_count = arg->ts_count;
00138     strncpy(l->name, arg->name, L4CPU_RESERVE_UTCB_NAME_LEN);
00139     /* allocate monitors */
00140     if((l->hists = calloc(sizeof(rt_mon_histogram_t*),
00141                           arg->ts_count))==0){
00142         err = -L4_ENOMEM;
00143         goto e_utcb;
00144     }
00145     for(i=0;i<arg->ts_count;i++){
00146         char buf[L4CPU_RESERVE_UTCB_NAME_LEN];
00147         if(arg->ts_count>2){
00148             snprintf(buf, sizeof(buf), l4util_idfmt"/%d",
00149                      l4util_idstr(arg->thread), i);
00150         } else {
00151             snprintf(buf, sizeof(buf), "%s /%s",
00152                      arg->name, i?"RT":"BE");
00153         }
00154         l->hists[i] = rt_mon_hist_create(0, arg->maxtime, 200, buf,
00155                                          "us", "cnt", RT_MON_TSC_TO_US_TIME);
00156     }       
00157 
00158     l->utcb = arg->utcb;
00159     l->next = utcb_list;
00160     utcb_list = l;
00161 
00162     return 0;
00163 
00164   e_utcb:
00165     free(l);
00166     return err;
00167 }
00168 
00169 /*!\brief Actually remove a thread from the list of watched threads
00170  */
00171 static int watch_del_utcb_do(const l4cpu_reserve_utcb_t *utcb){
00172     utcb_list_t *l, *o=0;
00173 
00174     for(l=utcb_list; l; l=l->next){
00175         if(l->utcb==utcb){
00176             /* deallocate monitors */
00177             int i;
00178             for(i=0; i<l->ts_count;i++){
00179                 rt_mon_hist_free(l->hists[i]);
00180             }
00181             free(l->hists);
00182             if (o)
00183               o->next = l->next;
00184             else
00185               utcb_list = l->next;
00186             free(l);
00187             return 0;
00188         }
00189         o=l;
00190     }
00191     return -L4_ENOTFOUND;
00192 }
00193 
00194 
00195 /*!\brief Helper function to add a sample to a timeslice of a thread
00196  *
00197  * \param l             utcb-list describing the thread
00198  * \param id            timeslice id
00199  * \param time          consumed time of the timeslice in the current period
00200  */
00201 static void watch_insert_sample(utcb_list_t *l, int id, long time){
00202     rt_mon_hist_insert_data(l->hists[id], time, 1);
00203 /*
00204     printf("Adding sample for "l4util_idfmt"/%d, period %lu, %d us\n",
00205            l4util_idstr(l->thread), id, l->period, time);
00206 */
00207 }
00208 
00209 /*!\brief Add a history element: end of reservation/next reservation
00210  *
00211  * This function sets the history element of the given id to the
00212  * given time. For all rt-timeslices below the given one, a history-element
00213  * with time 0 will be added.
00214  */
00215 static int watch_add_res_entry(utcb_list_t *l, int id,
00216                                unsigned long long release,
00217                                long time){
00218     int i;
00219 
00220     /* ignore timeslices we do not know about */
00221     if(id>l->ts_count) return -1;
00222 
00223     if(l->release!=release){
00224         /* new period */
00225         l->release = release;
00226         l->ts_num=0;
00227         l->period++;
00228     }
00229     for(i=l->ts_num; i<id; i++){
00230         if(i) watch_insert_sample(l, i, 0);
00231     }
00232     watch_insert_sample(l, id, time);
00233     if(id>l->ts_num) l->ts_num = id;
00234     return 0;
00235 }
00236 
00237 /*!\brief Add a history element: end of period/next period
00238  *
00239  * This function sets the history element of the given id to the given
00240  * time. For all rt-timeslices that got no reservation in this period,
00241  * a history-element with time 0 will be added.
00242  */
00243 static void watch_add_period_entry(utcb_list_t *l, int id,
00244                                    unsigned long long release,
00245                                    long time){
00246     int i;
00247 
00248     if(watch_add_res_entry(l, id, release, time)) return;
00249 
00250     for(i=l->ts_num+1; i<l->ts_count; i++){
00251         watch_insert_sample(l, i, 0);
00252     }
00253     l->ts_num = l->ts_count;
00254 }
00255 
00256 /*!\brief Add a history element: end of period/next period
00257  *
00258  * This function sets the history element of the given id to the given
00259  * time. For all rt-timeslices that got no reservation in this period,
00260  * a history-element with time 0 will be added.
00261  */
00262 static void watch_add_lost(utcb_list_t *l, int count){
00263     int i;
00264 
00265     for(i=0;i<l->ts_count;i++){
00266         rt_mon_hist_insert_lost(l->hists[i], count);
00267     }
00268 }
00269 
00270 /*!\brief Dump a scheduling element to the console
00271  */
00272 static void print_sched_event(int id, l4cpu_reserve_utcb_elem_t*stat)
00273     __attribute__((unused));
00274 static void print_sched_event(int id, l4cpu_reserve_utcb_elem_t*stat){
00275     printf("(%2d): type=%s, id=%d, release=%d, used=%d\n",
00276            id, l4cpu_reserve_utcb_stat_itoa(stat->type), stat->id,
00277            (int)stat->release, (int)stat->left);
00278 }
00279 
00280 /*!\brief Poll all watched threads for new events
00281  */
00282 static void watch_eval_utcbs(void){
00283     utcb_list_t *l;
00284     l4cpu_reserve_utcb_t *utcb;
00285     int i;
00286 
00287     for(l=utcb_list; l; l=l->next){
00288         utcb = l->utcb;
00289         for(i = utcb->tail ; i!=utcb->head ;
00290             i = (i + 1) % L4CPU_RESERVE_UTCB_STAT_COUNT) {
00291 
00292 
00293             // print_sched_event(i, utcb->stat+i);
00294 
00295             if(utcb->stat[i].release){
00296                 switch(utcb->stat[i].type){
00297                 case L4_UTCB_PERIOD_OVERRUN:
00298                 case L4_UTCB_NEXT_PERIOD:
00299                     watch_add_period_entry(l, utcb->stat[i].id,
00300                                            utcb->stat[i].release,
00301                                            utcb->stat[i].left);
00302                     break;
00303                 case L4_UTCB_RESERVATION_OVERRUN:
00304                 case L4_UTCB_NEXT_RESERVATION:
00305                     watch_add_res_entry(l, utcb->stat[i].id,
00306                                         utcb->stat[i].release,
00307                                         utcb->stat[i].left);
00308                     break;
00309                 }
00310             }
00311         }
00312         if(utcb->full){
00313             watch_add_lost(l, utcb->full);
00314             utcb->full=0;
00315         }
00316         utcb->tail = i;
00317     }
00318 }
00319 
00320 /*!\brief The main watcher thread
00321  *
00322  * This thread waits for requests from other threads to add/remove threads
00323  * and periodically polls the watched threads for events.
00324  */
00325 static void watch_thread(void*arg){
00326     int err=0;
00327     l4_msgdope_t result;
00328     l4_threadid_t sender;
00329     l4_umword_t dw1;
00330     watch_cmd_t cmd;
00331     l4_timeout_t to;
00332 
00333     to = l4_timeout(L4_IPC_TIMEOUT_NEVER, l4util_micros2l4to((int)arg));
00334 
00335     while(1){
00336         if(err==L4_IPC_RETIMEOUT) watch_eval_utcbs();
00337         err = l4_ipc_wait(&sender, L4_IPC_SHORT_MSG,
00338                           (l4_umword_t*)&cmd, &dw1, to, &result);
00339         while(1){
00340             if(err) break;
00341             if(!l4_task_equal(sender, watch_thread_id)) break;
00342             switch(cmd){
00343             case WATCH_CMD_ADD:
00344                 dw1=watch_add_utcb_do((watch_arg_t*)dw1);
00345                 break;
00346             case WATCH_CMD_DEL:
00347                 dw1=watch_del_utcb_do((l4cpu_reserve_utcb_t*)dw1);
00348                 break;
00349             default:
00350                 dw1=-L4_EINVAL;
00351             } /* switch cmd.v.cmd */
00352             err = l4_ipc_reply_and_wait(sender, L4_IPC_SHORT_MSG,
00353                                         dw1, 0,
00354                                         &sender,
00355                                         L4_IPC_SHORT_MSG,
00356                                         (l4_umword_t*)&cmd, &dw1,
00357                                         to, &result);
00358         }
00359     }
00360 }
00361 

CPU reservation server Reference Manual, written by Jork Loeser  © 2004