void rdecl cpu_free_perfregs( THREAD *thp ) { if ( thp->cpu.pcr == &disabled_perfregs ) { return; } _sfree(thp->cpu.pcr,sizeof(*thp->cpu.pcr)); thp->cpu.pcr = &disabled_perfregs; }
static int mem_close_ocb(resmgr_context_t *ctp, void *reserved, void *vocb) { struct mem_ocb *ocb = vocb; if(ocb) { OBJECT *obp; if((obp = ocb->object)) { //RUSH3: Should get rid of memmgr_tymem_close() and //RUSH3: do everything it does in tymem_close() if(obp->hdr.type == OBJECT_MEM_TYPED) { memmgr_tymem_close(obp); } pathmgr_object_done(obp); } _sfree(ocb, sizeof *ocb); } return EOK; }
int tymem_open(resmgr_context_t *ctp, io_open_t *msg, void *handle, void *extra) { struct mem_ocb *ocb; unsigned tflags; unsigned count; if (msg->connect.path_len + sizeof(*msg) >= ctp->msg_max_size) { return ENAMETOOLONG; } if((msg->connect.eflag & _IO_CONNECT_EFLAG_DIR) || msg->connect.path[0] == '\0') { if(msg->connect.ioflag & _IO_FLAG_WR) { return EISDIR; } //RUSH3: Simply fakes "/dev/tymem" as empty copy of "/dev/shmem". if(resmgr_open_bind(ctp, 0, &mem_io_funcs) == -1) { return errno; } return EOK; } if(msg->connect.extra_type != _IO_CONNECT_EXTRA_TYMEM) { return ENOTSUP; } if(msg->connect.extra_len != sizeof(uint32_t)) { return ENOTSUP; } if(msg->connect.ioflag & ~(O_ACCMODE | O_CREAT | O_NOCTTY)) { return EINVAL; } #define ALL_TMEM (POSIX_TYPED_MEM_ALLOCATE|POSIX_TYPED_MEM_ALLOCATE_CONTIG|POSIX_TYPED_MEM_MAP_ALLOCATABLE) tflags = *(uint32_t *)extra; count = 0; if(tflags & POSIX_TYPED_MEM_ALLOCATE) ++count; if(tflags & POSIX_TYPED_MEM_ALLOCATE_CONTIG) ++count; if(tflags & POSIX_TYPED_MEM_MAP_ALLOCATABLE) ++count; if((count > 1) || (tflags & ~ALL_TMEM)) { return EINVAL; } if(!(tflags & (POSIX_TYPED_MEM_ALLOCATE|POSIX_TYPED_MEM_ALLOCATE_CONTIG))) { if(!proc_isaccess(0, &ctp->info)) { // Only root processes are allowed to specify direct offsets return EPERM; } } ocb = _scalloc(sizeof *ocb); if(ocb == NULL) { errno = ENOMEM; goto fail1; } ocb->tflags = tflags; ocb->ioflag = msg->connect.ioflag; errno = memmgr_tymem_open(msg->connect.path, ocb->tflags, &ocb->object, proc_lookup_pid(ctp->info.pid)); if(errno != EOK) goto fail2; //RUSH1: need permission checking on path ctp->id = mem_handle; if(resmgr_open_bind(ctp, ocb, &tymem_io_funcs) == -1) goto fail3; pathmgr_object_clone(ocb->object); return EOK; fail3: memmgr_tymem_close(ocb->object); fail2: _sfree(ocb, sizeof(*ocb)); fail1: return errno; }
void iofunc_ocb_free (IOFUNC_OCB_T *ocb) { _sfree(ocb, sizeof(*ocb)); }
static void kerext_process_destroy(void *data) { PROCESS *prp, *parent, *child; pid_t pid = (pid_t)data; THREAD *act = actives[KERNCPU]; lock_kernel(); if(!(prp = vector_lookup(&process_vector, PINDEX(pid)))) { kererr(act, ESRCH); return; } if(prp->querying) { // Some proc thread is looking at the process fields // (a QueryOject() has been done on it). We have to wait // before we can destroy the process and free the memory SETKSTATUS(act, 0); return; } _TRACE_PR_EMIT_DESTROY(prp, pid); // retarget all children to PROC if((child = prp->child)) { PROCESS *proc = sysmgr_prp; do { //Loop might take a while - give intr queue a chance to drain. KEREXT_PREEMPT(act); prp->child = child->sibling; if(procmgr.process_threads_destroyed) { if (((child->flags & (_NTO_PF_LOADING | _NTO_PF_TERMING | _NTO_PF_ZOMBIE)) == _NTO_PF_ZOMBIE) || (child->flags & _NTO_PF_NOZOMBIE)) { struct sigevent ev; child->flags &= ~(_NTO_PF_ZOMBIE | _NTO_PF_WAITINFO); (*procmgr.process_threads_destroyed)(child, &ev); sigevent_proc(&ev); } } child->parent = proc; child->sibling = proc->child; proc->child = child; --prp->num_processes; ++proc->num_processes; } while((child = prp->child)); } vector_rem(&process_vector, PINDEX(pid)); // Remove the thread vector if(prp->threads.nentries && prp->threads.nentries == prp->threads.nfree) vector_free(&prp->threads); // Remove the timer vector if(prp->timers.nentries && prp->timers.nentries == prp->timers.nfree) vector_free(&prp->timers); // Remove the fd vector if(prp->fdcons.nentries && prp->fdcons.nentries == prp->fdcons.nfree) vector_free(&prp->fdcons); // Remove the channel vector if(prp->chancons.nentries && prp->chancons.nentries == prp->chancons.nfree) vector_free(&prp->chancons); // Unlink the credential if(prp->cred) { cred_set(&prp->cred, NULL); } // undo all session stuff if(prp->session && atomic_sub_value(&prp->session->links, 1) == 1) { _sfree(prp->session, sizeof *prp->session); } prp->session = 0; // Unlink the limits if(prp->limits && --prp->limits->links == 0) { LINK1_REM(limits_list, prp->limits, LIMITS); object_free(NULL, &limits_souls, prp->limits); } if(prp->limits && prp->limits->links == ~0U) crash(); if(prp->pid) crash(); if(prp->cred) crash(); if(prp->alarm) crash(); if(pril_first(&prp->sig_pending)) crash(); if(prp->sig_table) crash(); if(prp->nfds) crash(); if(prp->chancons.vector) crash(); if(prp->fdcons.vector) crash(); if(prp->threads.vector) crash(); if(prp->timers.vector) crash(); if(prp->memory) crash(); if(prp->join_queue) crash(); // if(prp->session) crash(); if(prp->debugger) crash(); if(prp->lock) crash(); if(prp->num_active_threads) crash(); if(prp->vfork_info) crash(); // FIX ME - this is not NULL now ... why? if(prp->rsrc_list) crash(); if(prp->conf_table) crash(); // Unlink from parent parent = prp->parent; --parent->num_processes; if(prp == parent->child) { parent->child = prp->sibling; } else { for(parent = parent->child ; parent ; parent = parent->sibling) { if(parent->sibling == prp) { parent->sibling = prp->sibling; break; } } } //Keep pids as positive numbers pid_unique = (pid_unique + (PID_MASK + 1)) & INT_MAX; /* diassociate prp from all partitions */ /* apparently prp->pid gets looked at even though we are about to object_free(prp). prp->pid = pid; // stick it back in so we have the info for the disassociate event */ (void)SCHEDPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MEMPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MUTEX_DESTROY(prp, &prp->mpartlist_lock); (void)MUTEX_DESTROY(prp, &prp->spartlist_lock); CRASHCHECK(prp->mpart_list.vector != NULL); CRASHCHECK(prp->spart_list != NULL); // no vector for sched partitions since only 1 object_free(NULL, &process_souls, prp); SETKSTATUS(act, 1); }
/** // // By design this function will only be called by a thread in the same // process as the one being destroyed. It might very well be the same // thread destroying itself. The code does not currently require this // but it is good to remember this for the future. //*/ void rdecl thread_destroy(THREAD *thp) { PROCESS *prp = thp->process; PULSE *pup; int i; SYNC *syp; chk_lock(); _TRACE_TH_EMIT_DESTROY(thp); if(thp->state != STATE_DEAD) { if(thp->state == STATE_RUNNING && thp != actives[KERNCPU]) { // The thread is running on another processor in an SMP system. thp->flags |= (_NTO_TF_KILLSELF | _NTO_TF_ONLYME); SENDIPI(thp->runcpu, IPI_RESCHED); return; } // Remove thread from any queues. if(!force_ready(thp, _FORCE_KILL_SELF)) { // Couldn't kill this thread right now, do it later return; } // Remove any fpu buffer before the thread becomes dead if(thp->fpudata) { FPU_REGISTERS *fpudata = FPUDATA_PTR(thp->fpudata); if(FPUDATA_INUSE(thp->fpudata)) { if(FPUDATA_CPU(thp->fpudata) != KERNCPU) { // Context still in use on another CPU; need to flush it out thp->flags |= (_NTO_TF_KILLSELF | _NTO_TF_ONLYME); SENDIPI(FPUDATA_CPU(thp->fpudata), IPI_CONTEXT_SAVE); return; } } if(actives_fpu[KERNCPU] == thp) { actives_fpu[KERNCPU] = NULL; } atomic_order(thp->fpudata = NULL); object_free(NULL, &fpu_souls, fpudata); } SIGMASK_ONES(&thp->sig_blocked); if(thp->flags & _NTO_TF_RCVINFO) { thp->args.ri.thp->restart = 0; } if(thp->flags & _NTO_TF_SHORT_MSG) { ((THREAD *)thp->blocked_on)->restart = 0; } thp->timeout_flags = 0; if(thp->timeout) { timer_free(prp, thp->timeout); thp->timeout = NULL; } // Now remove thread from the ready queue. // Call unready() instead of block() the thread may not be active. unready(thp, STATE_DEAD); // The thread is definitely no longer in actives[]. /* Give the vendor extension a chance to take first dibs */ if ( kerop_thread_destroy_hook != NULL ) { kerop_thread_destroy_hook(thp); } // purge mutex hold list while((syp = thp->mutex_holdlist)) { THREAD *waiting = pril_first(&syp->waiting); CRASHCHECK(waiting->args.mu.owner == 0); mutex_holdlist_rem(syp); waiting->args.mu.owner = 0; } // clear client field if(thp->client != 0) { /* need to clear client's server field */ thp->client->args.ms.server = 0; thp->client = 0; } // Release the stack if it was dynamically allocated if(thp->flags & _NTO_TF_ALLOCED_STACK) { if(prp->pid != PROCMGR_PID && procmgr.process_stack_code) { if(thp->state != STATE_STACK) { struct sigevent event; // Must do modification of user address spaces at process time thp->state = STATE_STACK; _TRACE_TH_EMIT_STATE(thp, STACK); thp->flags |= (_NTO_TF_KILLSELF | _NTO_TF_ONLYME); thp->flags &= ~(_NTO_TF_TO_BE_STOPPED | _NTO_TF_WAAA); event.sigev_notify = SIGEV_PULSE; event.sigev_coid = PROCMGR_COID; event.sigev_value.sival_int = SYNC_OWNER(thp); event.sigev_priority = thp->priority; event.sigev_code = procmgr.process_stack_code; if(sigevent_proc(&event)) { // Very bad; we are out of pulses... This means // there's absolutely no memory left in the system. CRASHCHECK(1); } } return; } procmgr_stack_free(thp); thp->flags &= ~_NTO_TF_ALLOCED_STACK; } // Make thread lookups invalid vector_flag(&prp->threads, thp->tid, 1); // If there is still active threads retarget valid_thp if(--prp->num_active_threads && prp->valid_thp == thp) { THREAD *thp2, *thp3 = NULL; for(i = 0; i < prp->threads.nentries; i++) { // VECP() will not match our thread. if(VECP(thp2, &prp->threads, i)) { prp->valid_thp = thp2; break; } else if((thp2 = VECP2(thp2, &prp->threads, i)) && thp2->state != STATE_DEAD) { thp3 = thp2; } } // Could happen if we only have threads starting up left if(prp->valid_thp == thp) { prp->valid_thp = thp3; } } else if(!prp->num_active_threads) { prp->valid_thp = NULL; } // If killing thread that last processed a signal, update // signal tid cache. if(thp->tid == prp->sigtid_cache) { THREAD *vthp = prp->valid_thp; prp->sigtid_cache = (vthp != NULL) ? vthp->tid : 0; } // Deactivate any timers targeting this thread for(i = 0; i < prp->timers.nentries; ++i) { TIMER *tip; if(VECP(tip, &prp->timers, i) && (tip->flags & _NTO_TI_ACTIVE) && (prp->num_active_threads == 0 || tip->thread == thp)) { if((tip->flags & _NTO_TI_TARGET_PROCESS) && (prp->valid_thp != NULL)) { // have to retarget to a new thread tip->thread = prp->valid_thp; } else { timer_deactivate(tip); } } } if(prp->alarm != NULL) { TIMER *tip = prp->alarm; // Alarm timers are always process based. if(tip->thread == thp) { tip->thread = prp->valid_thp; } } // Clean up after a SPORADIC thread if(thp->policy == SCHED_SPORADIC) { sched_ss_cleanup(thp); } // Release any attached interrupts if(prp->flags & _NTO_PF_CHECK_INTR) { INTERRUPT *itp; for(i = 0; i < interrupt_vector.nentries; ++i) { if(VECP(itp, &interrupt_vector, i) && itp->thread->process == prp) { // Only detach interrupt if bound to thread, or all threads are inactive if(prp->num_active_threads == 0) { interrupt_detach_entry(prp, i); } else if(itp->thread != thp) { /* nothing to do */ } else if(itp->flags & _NTO_INTR_FLAGS_PROCESS) { itp->thread = prp->valid_thp; } else { interrupt_detach_entry(prp, i); } } } } // timers/interrupts have been detached, so flush the interrupt // queues to get rid of anything pending that's pointing at // this thread intrevent_flush(); // Purge any enqueued signals for( ;; ) { pup = pril_first(&thp->sig_pending); if(pup == NULL) break; pril_rem(&thp->sig_pending, pup); object_free(prp, &pulse_souls, pup); } // If requested send a death pulse to a channel. if(prp->death_chp && (prp->death_chp->flags & _NTO_CHF_THREAD_DEATH)) { pulse_deliver(prp->death_chp, thp->real_priority, _PULSE_CODE_THREADDEATH, thp->tid+1, -1, 0); } } // A zombie if nobody waiting, not detached and not last thread. if(thp->join == NULL && (thp->flags & _NTO_TF_DETACHED) == 0 && (prp->threads.nentries-1 != prp->threads.nfree)) { return; } // Wakeup any thread waiting to join on me. if(thp->join) { THREAD *jthp = thp->join; if(jthp->state == STATE_WAITTHREAD) { // This thread was being created.... kererr(jthp, (intptr_t)thp->status); } else { // This is a normal thread death... jthp->args.jo.status = thp->status; jthp->flags |= _NTO_TF_JOIN; SETKSTATUS(jthp, EOK); } ready(jthp); } // FPU buffer should be freed already if(thp->fpudata) crash(); // Remove thread from process thread vector. thp = vector_rem(&prp->threads, thp->tid); if(thp == NULL) { crash(); } // Remove any CPU-specific save buffers cpu_thread_destroy(thp); #ifdef _mt_LTT_TRACES_ /* PDB */ //mt_TRACE_DEBUG("2 !"); mt_trace_task_delete(thp->process->pid, thp->tid, 0); #endif // Remember the priority for the pulses below i = thp->real_priority; thp->schedinfo.rr_ticks = 0; /* sanity check before we return it to the pool */ // Free the name if it was allocated if(thp->name != NULL) { int name_len = strlen(thp->name) + 1; if(name_len >= THREAD_NAME_FIXED_SIZE) { _sfree(thp->name, name_len); } else { object_free(thp->process, &threadname_souls, thp->name); } thp->name = NULL; } // Release thread object back to the free queue. object_free(prp, &thread_souls, thp); if(prp->threads.nentries == prp->threads.nfree) { // Purge any enqueued signals pending on the process. for( ;; ) { pup = pril_first(&prp->sig_pending); if(pup == NULL) break; pril_rem(&prp->sig_pending, pup); object_free(prp, &pulse_souls, pup); } // setup process so it can run as a terminator thread prp->flags |= _NTO_PF_TERMING; prp->boundry_addr = VM_KERN_SPACE_BOUNDRY; SIGMASK_ONES(&prp->sig_ignore); // If debugging, don't tell debugger, not process manager if(prp->debugger && (*debug_process_exit)(prp, i)) { return; } // If last thread gone, tell the process manager. if(procmgr.process_threads_destroyed) { struct sigevent ev; /* PDB process_delete */ _TRACE_DESTROY_EH(prp); (*procmgr.process_threads_destroyed)(prp, &ev); sigevent_proc(&ev); } } }