Prex Home / Browse Source - Prex Version: 0.9.0

root/sys/kern/task.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. task_create
  2. task_terminate
  3. task_self
  4. task_suspend
  5. task_resume
  6. task_setname
  7. task_setcap
  8. task_chkcap
  9. task_capable
  10. task_valid
  11. task_access
  12. task_info
  13. task_bootstrap
  14. task_init

   1 /*-
   2  * Copyright (c) 2005-2009, Kohsuke Ohtani
   3  * All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 1. Redistributions of source code must retain the above copyright
   9  *    notice, this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright
  11  *    notice, this list of conditions and the following disclaimer in the
  12  *    documentation and/or other materials provided with the distribution.
  13  * 3. Neither the name of the author nor the names of any co-contributors
  14  *    may be used to endorse or promote products derived from this software
  15  *    without specific prior written permission.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  */
  29 
  30 /*
  31  * task.c - task management routines.
  32  */
  33 
  34 #include <kernel.h>
  35 #include <kmem.h>
  36 #include <sched.h>
  37 #include <thread.h>
  38 #include <ipc.h>
  39 #include <sync.h>
  40 #include <vm.h>
  41 #include <exception.h>
  42 #include <task.h>
  43 #include <hal.h>
  44 #include <sys/bootinfo.h>
  45 
  46 struct task             kernel_task;    /* kernel task */
  47 static struct list      task_list;      /* list for all tasks */
  48 static int              ntasks;         /* number of tasks in system */
  49 
  50 /**
  51  * task_create - create a new task.
  52  *
  53  * vm_option:
  54  *   VM_NEW:   The child task will have fresh memory image.
  55  *   VM_SHARE: The child task will share whole memory image with parent.
  56  *   VM_COPY:  The parent's memory image is copied to the child's one.
  57  *             VM_COPY is supported only with MMU system.
  58  *
  59  * Note: The child task initially contains no threads.
  60  */
  61 int
  62 task_create(task_t parent, int vm_option, task_t *childp)
  63 {
  64         struct task *task;
  65         vm_map_t map = NULL;
  66 
  67         ASSERT(parent != NULL);
  68 
  69         switch (vm_option) {
  70         case VM_NEW:
  71         case VM_SHARE:
  72 #ifdef CONFIG_MMU
  73         case VM_COPY:
  74 #endif
  75                 break;
  76         default:
  77                 return EINVAL;
  78         }
  79         if (ntasks >= MAXTASKS)
  80                 return EAGAIN;
  81 
  82         sched_lock();
  83         if (!task_valid(parent)) {
  84                 sched_unlock();
  85                 return ESRCH;
  86         }
  87         if ((curtask->flags & TF_SYSTEM) == 0) {
  88                 if (!task_access(parent)) {
  89                         sched_unlock();
  90                         return EPERM;
  91                 }
  92                 /*
  93                  * It's important to set zero as task id before
  94                  * copying parent's memory space. Otherwise, we
  95                  * have to switch VM space to copy it.
  96                  */
  97                 task = 0;
  98                 if (copyout(&task, childp, sizeof(task))) {
  99                         sched_unlock();
 100                         return EFAULT;
 101                 }
 102         }
 103 
 104         if ((task = kmem_alloc(sizeof(*task))) == NULL) {
 105                 sched_unlock();
 106                 return ENOMEM;
 107         }
 108         memset(task, 0, sizeof(*task));
 109 
 110         /*
 111          * Setup VM mapping.
 112          */
 113         switch (vm_option) {
 114         case VM_NEW:
 115                 map = vm_create();
 116                 break;
 117         case VM_SHARE:
 118                 vm_reference(parent->map);
 119                 map = parent->map;
 120                 break;
 121         case VM_COPY:
 122                 map = vm_dup(parent->map);
 123                 break;
 124         }
 125         if (map == NULL) {
 126                 kmem_free(task);
 127                 sched_unlock();
 128                 return ENOMEM;
 129         }
 130 
 131         /*
 132          * Fill initial task data.
 133          */
 134         task->map = map;
 135         task->handler = parent->handler;
 136         task->capability = parent->capability;
 137         task->parent = parent;
 138         task->flags = TF_DEFAULT;
 139         strlcpy(task->name, "*noname", MAXTASKNAME);
 140         list_init(&task->threads);
 141         list_init(&task->objects);
 142         list_init(&task->mutexes);
 143         list_init(&task->conds);
 144         list_init(&task->sems);
 145         list_insert(&task_list, &task->link);
 146         ntasks++;
 147 
 148         if (curtask->flags & TF_SYSTEM)
 149                 *childp = task;
 150         else {
 151                 /*
 152                  * No page fault here because we have already
 153                  * checked it.
 154                  */
 155                 copyout(&task, childp, sizeof(task));
 156         }
 157 
 158         sched_unlock();
 159         return 0;
 160 }
 161 
 162 /*
 163  * Terminate the specified task.
 164  */
 165 int
 166 task_terminate(task_t task)
 167 {
 168         list_t head, n;
 169         thread_t t;
 170 
 171         sched_lock();
 172         if (!task_valid(task)) {
 173                 sched_unlock();
 174                 return ESRCH;
 175         }
 176         if (!task_access(task)) {
 177                 sched_unlock();
 178                 return EPERM;
 179         }
 180 
 181         list_remove(&task->link);
 182         task->handler = EXC_DFL;
 183 
 184         /*
 185          * Clean up all resources owned by the target task.
 186          */
 187         timer_stop(&task->alarm);
 188         object_cleanup(task);
 189         mutex_cleanup(task);
 190         cond_cleanup(task);
 191         sem_cleanup(task);
 192 
 193         /*
 194          * Terminate each thread in the task.
 195          */
 196         head = &task->threads;
 197         for (n = list_first(head); n != head; n = list_next(n)) {
 198                 t = list_entry(n, struct thread, task_link);
 199                 if (t != curthread)
 200                         thread_destroy(t);
 201         }
 202         if (task == curtask)
 203                 thread_destroy(curthread);
 204 
 205         vm_terminate(task->map);
 206         task->map = NULL;
 207         kmem_free(task);
 208         ntasks--;
 209         sched_unlock();
 210         return 0;
 211 }
 212 
 213 /*
 214  * Return the current task.
 215  */
 216 task_t
 217 task_self(void)
 218 {
 219 
 220         return curthread->task;
 221 }
 222 
 223 /*
 224  * Suspend a task.
 225  */
 226 int
 227 task_suspend(task_t task)
 228 {
 229         list_t head, n;
 230         thread_t t;
 231 
 232         sched_lock();
 233         if (!task_valid(task)) {
 234                 sched_unlock();
 235                 return ESRCH;
 236         }
 237         if (!task_access(task)) {
 238                 sched_unlock();
 239                 return EPERM;
 240         }
 241 
 242         if (++task->suscnt == 1) {
 243                 /*
 244                  * Suspend all threads within the task.
 245                  */
 246                 head = &task->threads;
 247                 for (n = list_first(head); n != head; n = list_next(n)) {
 248                         t = list_entry(n, struct thread, task_link);
 249                         thread_suspend(t);
 250                 }
 251         }
 252         sched_unlock();
 253         return 0;
 254 }
 255 
 256 /*
 257  * Resume a task.
 258  *
 259  * A thread in the task will begin to run only when both
 260  * thread suspend count and task suspend count become 0.
 261  */
 262 int
 263 task_resume(task_t task)
 264 {
 265         list_t head, n;
 266         thread_t t;
 267 
 268         ASSERT(task != curtask);
 269 
 270         sched_lock();
 271         if (!task_valid(task)) {
 272                 sched_unlock();
 273                 return ESRCH;
 274         }
 275         if (!task_access(task)) {
 276                 sched_unlock();
 277                 return EPERM;
 278         }
 279         if (task->suscnt == 0) {
 280                 sched_unlock();
 281                 return EINVAL;
 282         }
 283 
 284         if (--task->suscnt == 0) {
 285                 /*
 286                  * Resume all threads in the target task.
 287                  */
 288                 head = &task->threads;
 289                 for (n = list_first(head); n != head; n = list_next(n)) {
 290                         t = list_entry(n, struct thread, task_link);
 291                         thread_resume(t);
 292                 }
 293         }
 294         sched_unlock();
 295         return 0;
 296 }
 297 
 298 /*
 299  * Set task name.
 300  *
 301  * The naming service is separated from task_create() because
 302  * the task name can be changed at anytime by exec().
 303  */
 304 int
 305 task_setname(task_t task, const char *name)
 306 {
 307         char str[MAXTASKNAME];
 308         int error;
 309 
 310         sched_lock();
 311         if (!task_valid(task)) {
 312                 sched_unlock();
 313                 return ESRCH;
 314         }
 315         if (!task_access(task)) {
 316                 sched_unlock();
 317                 return EPERM;
 318         }
 319 
 320         if (curtask->flags & TF_SYSTEM)
 321                 strlcpy(task->name, name, MAXTASKNAME);
 322         else {
 323                 error = copyinstr(name, str, MAXTASKNAME);
 324                 if (error) {
 325                         sched_unlock();
 326                         return error;
 327                 }
 328                 strlcpy(task->name, str, MAXTASKNAME);
 329         }
 330         sched_unlock();
 331         return 0;
 332 }
 333 
 334 /*
 335  * Set the capability of the specified task.
 336  */
 337 int
 338 task_setcap(task_t task, cap_t cap)
 339 {
 340 
 341         if (!task_capable(CAP_SETPCAP))
 342                 return EPERM;
 343 
 344         sched_lock();
 345         if (!task_valid(task)) {
 346                 sched_unlock();
 347                 return ESRCH;
 348         }
 349         if (!task_access(task)) {
 350                 sched_unlock();
 351                 return EPERM;
 352         }
 353         task->capability = cap;
 354         sched_unlock();
 355         return 0;
 356 }
 357 
 358 /*
 359  * task_chkcap - system call to check task capability.
 360  */
 361 int
 362 task_chkcap(task_t task, cap_t cap)
 363 {
 364         int error = 0;
 365 
 366         sched_lock();
 367         if (!task_valid(task)) {
 368                 sched_unlock();
 369                 return ESRCH;
 370         }
 371         if ((task->capability & cap) == 0) {
 372                 DPRINTF(("Denying capability by %s: task=%s cap=%08x\n",
 373                          curtask->name, task->name, cap));
 374                 if (task->flags & TF_AUDIT)
 375                         panic("audit failed");
 376                 error = EPERM;
 377         }
 378         sched_unlock();
 379         return error;
 380 }
 381 
 382 /*
 383  * Check if the current task has specified capability.
 384  * Returns true on success, or false on error.
 385  */
 386 int
 387 task_capable(cap_t cap)
 388 {
 389         int capable = 1;
 390 
 391         if ((curtask->capability & cap) == 0) {
 392                 DPRINTF(("Denying capability by kernel: task=%s cap=%08x\n",
 393                          curtask->name, cap));
 394                 if (curtask->flags & TF_AUDIT)
 395                         panic("audit failed");
 396                 capable = 0;
 397         }
 398         return capable;
 399 }
 400 
 401 /*
 402  * Return true if the specified task is valid.
 403  */
 404 int
 405 task_valid(task_t task)
 406 {
 407         task_t tmp;
 408         list_t n;
 409 
 410         for (n = list_first(&task_list); n != &task_list; n = list_next(n)) {
 411                 tmp = list_entry(n, struct task, link);
 412                 if (tmp == task)
 413                         return 1;
 414         }
 415         return 0;
 416 }
 417 
 418 /*
 419  * Check if the current task can access the specified task.
 420  * Return true on success, or false on error.
 421  */
 422 int
 423 task_access(task_t task)
 424 {
 425 
 426         if (task->flags & TF_SYSTEM) {
 427                 /* Do not access the kernel task. */
 428                 return 0;
 429         } else {
 430                 if (task == curtask || task->parent == curtask ||
 431                     task == curtask->parent ||  /* XXX: fork on nommu */
 432                     task_capable(CAP_TASKCTRL))
 433                         return 1;
 434         }
 435         return 0;
 436 }
 437 
 438 int
 439 task_info(struct taskinfo *info)
 440 {
 441         u_long target = info->cookie;
 442         u_long i = 0;
 443         task_t task;
 444         list_t n;
 445 
 446         sched_lock();
 447         n = list_first(&task_list);
 448         do {
 449                 if (i++ == target) {
 450                         task = list_entry(n, struct task, link);
 451                         info->cookie = i;
 452                         info->id = task;
 453                         info->flags = task->flags;
 454                         info->suscnt = task->suscnt;
 455                         info->capability = task->capability;
 456                         info->vmsize = task->map->total;
 457                         info->nthreads = task->nthreads;
 458                         info->active = (task == curtask) ? 1 : 0;
 459                         strlcpy(info->taskname, task->name, MAXTASKNAME);
 460                         sched_unlock();
 461                         return 0;
 462                 }
 463                 n = list_next(n);
 464         } while (n != &task_list);
 465         sched_unlock();
 466         return ESRCH;
 467 }
 468 
 469 /*
 470  * Create and setup boot tasks.
 471  */
 472 void
 473 task_bootstrap(void)
 474 {
 475         struct module *mod;
 476         struct bootinfo *bi;
 477         task_t task;
 478         thread_t t;
 479         void *stack, *sp;
 480         int i, error = 0;
 481 
 482         machine_bootinfo(&bi);
 483         mod = &bi->tasks[0];
 484 
 485         for (i = 0; i < bi->nr_tasks; i++) {
 486                 /*
 487                  * Create a new task.
 488                  */
 489                 if ((error = task_create(&kernel_task, VM_NEW, &task)) != 0)
 490                         break;
 491                 if ((error = vm_load(task->map, mod, &stack)) != 0)
 492                         break;
 493                 task_setname(task, mod->name);
 494 
 495                 /*
 496                  * Set the default capability.
 497                  * We give CAP_SETPCAP to the exec server.
 498                  */
 499                 task->capability = CAPSET_BOOT;
 500                 if (!strncmp(task->name, "exec", MAXTASKNAME))
 501                         task->capability |= CAP_SETPCAP;
 502 
 503                 /*
 504                  * Create and start a new thread.
 505                  */
 506                 if ((error = thread_create(task, &t)) != 0)
 507                         break;
 508                 sp = (char *)stack + DFLSTKSZ - (sizeof(int) * 3);
 509                 error = thread_load(t, (void (*)(void))mod->entry, sp);
 510                 if (error)
 511                         break;
 512                 t->priority = PRI_REALTIME;
 513                 t->basepri = PRI_REALTIME;
 514                 thread_resume(t);
 515 
 516                 mod++;
 517         }
 518         if (error) {
 519                 DPRINTF(("task_bootstrap: error=%d\n", error));
 520                 panic("unable to load boot task");
 521         }
 522 }
 523 
 524 /*
 525  * Initialize task.
 526  */
 527 void
 528 task_init(void)
 529 {
 530 
 531         list_init(&task_list);
 532 
 533         /*
 534          * Create a kernel task as first task.
 535          */
 536         strlcpy(kernel_task.name, "kernel", MAXTASKNAME);
 537         kernel_task.flags = TF_SYSTEM;
 538         kernel_task.nthreads = 0;
 539         list_init(&kernel_task.threads);
 540         list_init(&kernel_task.objects);
 541         list_init(&kernel_task.mutexes);
 542         list_init(&kernel_task.conds);
 543         list_init(&kernel_task.sems);
 544 
 545         list_insert(&task_list, &kernel_task.link);
 546         ntasks = 1;
 547 }

/* [<][>][^][v][top][bottom][index][help] */