Example #1
0
int pthread_getschedparam(pthread_t thread, FAR int *policy, FAR struct sched_param *param)
{
	int ret;

	svdbg("Thread ID=%d policy=0x%p param=0x%p\n", thread, policy, param);

	if (!policy || !param) {
		ret = EINVAL;
	} else {
		/* Get the schedparams of the thread. */

		ret = sched_getparam((pid_t)thread, param);
		if (ret != OK) {
			ret = EINVAL;
		}

		/* Return the policy. */

		*policy = sched_getscheduler((pid_t)thread);
		if (*policy == ERROR) {
			ret = get_errno();
		}
	}

	svdbg("Returning %d\n", ret);
	return ret;
}
int spawn_execattrs(pid_t pid, FAR const posix_spawnattr_t *attr)
{
  struct sched_param param;

  DEBUGASSERT(attr);

  /* Now set the attributes.  Note that we ignore all of the return values
   * here because we have already successfully started the task.  If we
   * return an error value, then we would also have to stop the task.
   */

  /* If we are only setting the priority, then call sched_setparm()
   * to set the priority of the of the new task.
   */

  if ((attr->flags & POSIX_SPAWN_SETSCHEDPARAM) != 0)
    {
      /* Get the priority from the attrributes */

      param.sched_priority = attr->priority;

      /* If we are setting *both* the priority and the scheduler,
       * then we will call sched_setscheduler() below.
       */

      if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) == 0)
        {
          svdbg("Setting priority=%d for pid=%d\n",
                param.sched_priority, pid);

          (void)sched_setparam(pid, &param);
        }
    }

  /* If we are only changing the scheduling policy, then reset
   * the priority to the default value (the same as this thread) in
   * preparation for the sched_setscheduler() call below.
   */

  else if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0)
    {
      (void)sched_getparam(0, &param);
    }

  /* Are we setting the scheduling policy?  If so, use the priority
   * setting determined above.
   */

  if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0)
    {
      svdbg("Setting policy=%d priority=%d for pid=%d\n",
            attr->policy, param.sched_priority, pid);

      (void)sched_setscheduler(pid, attr->policy, &param);
    }

  return OK;
}
Example #3
0
void up_sigdeliver(void)
{
	struct tcb_s *rtcb = this_task();
	uint32_t regs[XCPTCONTEXT_REGS];
	sig_deliver_t sigdeliver;

	/* Save the errno.  This must be preserved throughout the signal handling
	 * so that the user code final gets the correct errno value (probably
	 * EINTR).
	 */

	int saved_errno = rtcb->pterrno;

	board_autoled_on(LED_SIGNAL);

	svdbg("rtcb=%p sigdeliver=%p sigpendactionq.head=%p\n", rtcb, rtcb->xcp.sigdeliver, rtcb->sigpendactionq.head);
	ASSERT(rtcb->xcp.sigdeliver != NULL);

	/* Save the real return state on the stack. */

	up_copyfullstate(regs, rtcb->xcp.regs);
	regs[REG_PC] = rtcb->xcp.saved_pc;
	regs[REG_CPSR] = rtcb->xcp.saved_cpsr;

	/* Get a local copy of the sigdeliver function pointer. we do this so that
	 * we can nullify the sigdeliver function pointer in the TCB and accept
	 * more signal deliveries while processing the current pending signals.
	 */

	sigdeliver = rtcb->xcp.sigdeliver;
	rtcb->xcp.sigdeliver = NULL;

	/* Then restore the task interrupt state */

	irqrestore(regs[REG_CPSR]);

	/* Deliver the signals */

	sigdeliver(rtcb);

	/* Output any debug messages BEFORE restoring errno (because they may
	 * alter errno), then disable interrupts again and restore the original
	 * errno that is needed by the user logic (it is probably EINTR).
	 */

	svdbg("Resuming\n");
	(void)irqsave();
	rtcb->pterrno = saved_errno;

	/* Then restore the correct state for this thread of execution. */

	board_autoled_off(LED_SIGNAL);
#ifdef CONFIG_TASK_SCHED_HISTORY
	/* Save the task name which will be scheduled */
	save_task_scheduling_status(rtcb);
#endif
	up_fullcontextrestore(regs);
}
int pthread_condattr_destroy(FAR pthread_condattr_t *attr)
{
	int ret = OK;

	svdbg("attr=0x%p\n", attr);

	if (!attr) {
		ret = EINVAL;
	}

	svdbg("Returning %d\n", ret);
	return ret;
}
Example #5
0
int pthread_cond_broadcast(FAR pthread_cond_t *cond)
{
	int ret = OK;
	int sval;

	svdbg("cond=0x%p\n", cond);

	if (!cond) {
		ret = EINVAL;
	} else {
		/* Disable pre-emption until all of the waiting threads have been
		 * restarted. This is necessary to assure that the sval behaves as
		 * expected in the following while loop
		 */

		sched_lock();

		/* Get the current value of the semaphore */

		if (sem_getvalue((sem_t *)&cond->sem, &sval) != OK) {
			ret = EINVAL;
		} else {
			/* Loop until all of the waiting threads have been restarted. */

			while (sval < 0) {
				/* If the value is less than zero (meaning that one or more
				 * thread is waiting), then post the condition semaphore.
				 * Only the highest priority waiting thread will get to execute
				 */

				ret = pthread_sem_give((sem_t *)&cond->sem);

				/* Increment the semaphore count (as was done by the
				 * above post).
				 */

				sval++;
			}
		}

		/* Now we can let the restarted threads run */

		sched_unlock();
	}

	svdbg("Returning %d\n", ret);
	return ret;
}
int pthread_attr_getinheritsched(FAR const pthread_attr_t *attr, FAR int *inheritsched)
{
	int ret;

	svdbg("attr=0x%p inheritsched=0x%p\n", attr, inheritsched);

	if (!attr || !inheritsched) {
		ret = EINVAL;
	} else {
		*inheritsched = (int)attr->inheritsched;
		ret = OK;
	}

	svdbg("Returning %d\n", ret);
	return ret;
}
Example #7
0
static inline int spawn_close(FAR struct spawn_close_file_action_s *action)
{
  /* The return value from close() is ignored */

  svdbg("Closing fd=%d\n", action->fd);

  (void)close(action->fd);
  return OK;
}
Example #8
0
int pthread_attr_init(FAR pthread_attr_t *attr)
{
	int ret = OK;

	svdbg("attr=0x%p\n", attr);
	if (!attr) {
		ret = ENOMEM;
	} else {
		/* Set the child thread priority to be the default
		 * priority. Set the child stack size to some arbitrary
		 * default value.
		 */

		memcpy(attr, &g_default_pthread_attr, sizeof(pthread_attr_t));
	}

	svdbg("Returning %d\n", ret);
	return ret;
}
Example #9
0
int pthread_cond_destroy(FAR pthread_cond_t *cond)
{
	int ret = OK;

	svdbg("cond=0x%p\n", cond);

	if (!cond) {
		ret = EINVAL;
	}

	/* Destroy the semaphore contained in the structure */

	else if (sem_destroy((sem_t *)&cond->sem) != OK) {
		ret = EINVAL;
	}

	svdbg("Returning %d\n", ret);
	return ret;
}
int pthread_attr_setschedpolicy(FAR pthread_attr_t *attr, int policy)
{
	int ret;

	svdbg("attr=0x%p policy=%d\n", attr, policy);

#if CONFIG_RR_INTERVAL > 0
	if (!attr || (policy != SCHED_FIFO && policy != SCHED_RR))
#else
	if (!attr || policy != SCHED_FIFO)
#endif
	{
		ret = EINVAL;
	} else {
		attr->policy = policy;
		ret = OK;
	}

	svdbg("Returning %d\n", ret);
	return ret;
}
Example #11
0
static inline int spawn_open(FAR struct spawn_open_file_action_s *action)
{
  int fd;
  int ret = OK;

  /* Open the file */

  svdbg("Open'ing path=%s oflags=%04x mode=%04x\n",
        action->path, action->oflags, action->mode);

  fd = open(action->path, action->oflags, action->mode);
  if (fd < 0)
    {
      ret = get_errno();
      sdbg("ERROR: open failed: %d\n", ret);
    }

  /* Does the return file descriptor happen to match the required file
   * desciptor number?
   */

  else if (fd != action->fd)
    {
      /* No.. dup2 to get the correct file number */

      svdbg("Dup'ing %d->%d\n", fd, action->fd);

      ret = dup2(fd, action->fd);
      if (ret < 0)
        {
          ret = get_errno();
          sdbg("ERROR: dup2 failed: %d\n", ret);
        }

      svdbg("Closing fd=%d\n", fd);
      close(fd);
    }

  return ret;
}
Example #12
0
int mod_load(FAR struct mod_loadinfo_s *loadinfo)
{
  int ret;

  svdbg("loadinfo: %p\n", loadinfo);
  DEBUGASSERT(loadinfo && loadinfo->filfd >= 0);

  /* Load section headers into memory */

  ret = mod_loadshdrs(loadinfo);
  if (ret < 0)
    {
      sdbg("ERROR: mod_loadshdrs failed: %d\n", ret);
      goto errout_with_buffers;
    }

  /* Determine total size to allocate */

  mod_elfsize(loadinfo);

  /* Allocate (and zero) memory for the ELF file. */

  /* Allocate memory to hold the ELF image */

  loadinfo->textalloc = (uintptr_t)kmm_zalloc(loadinfo->textsize + loadinfo->datasize);
  if (!loadinfo->textalloc)
    {
      sdbg("ERROR: Failed to allocate memory for the module\n");
      ret = -ENOMEM;
      goto errout_with_buffers;
    }

  loadinfo->datastart = loadinfo->textalloc + loadinfo->textsize;

  /* Load ELF section data into memory */

  ret = mod_loadfile(loadinfo);
  if (ret < 0)
    {
      sdbg("ERROR: mod_loadfile failed: %d\n", ret);
      goto errout_with_buffers;
    }

  return OK;

  /* Error exits */

errout_with_buffers:
  mod_unload(loadinfo);
  return ret;
}
Example #13
0
int pthread_completejoin(pid_t pid, FAR void *exit_value)
{
  FAR join_t *pjoin;

  svdbg("pid=%d exit_value=%p\n", pid, exit_value);

  /* First, find thread's structure in the private data set. */

  (void)pthread_takesemaphore(&g_join_semaphore);
  pjoin = pthread_findjoininfo(pid);
  if (!pjoin)
    {
      sdbg("Could not find join info, pid=%d\n", pid);
      (void)pthread_givesemaphore(&g_join_semaphore);
      return ERROR;
    }
  else
    {
      bool waiters;

      /* Save the return exit value in the thread structure. */

      pjoin->terminated = true;
      pjoin->exit_value = exit_value;

      /* Notify waiters of the availability of the exit value */

      waiters = pthread_notifywaiters(pjoin);

      /* If there are no waiters and if the thread is marked as detached.
       * then discard the join information now.  Otherwise, the pthread
       * join logic will call pthread_destroyjoin() when all of the threads
       * have sampled the exit value.
       */

      if (!waiters && pjoin->detached)
        {
           pthread_destroyjoin(pjoin);
        }

      /* Giving the following semaphore will allow the waiters
       * to call pthread_destroyjoin.
       */

      (void)pthread_givesemaphore(&g_join_semaphore);
    }

  return OK;
}
Example #14
0
int work_lpstart(void)
{
  pid_t pid;
  int wndx;

  /* Initialize work queue data structures */

  memset(&g_lpwork, 0, sizeof(struct kwork_wqueue_s));

  g_lpwork.delay = CONFIG_SCHED_LPWORKPERIOD / USEC_PER_TICK;
  dq_init(&g_lpwork.q);

  /* Don't permit any of the threads to run until we have fully initialized
   * g_lpwork.
   */

  sched_lock();

  /* Start the low-priority, kernel mode worker thread(s) */

  svdbg("Starting low-priority kernel worker thread(s)\n");

  for (wndx = 0; wndx < CONFIG_SCHED_LPNTHREADS; wndx++)
    {
       pid = kernel_thread(LPWORKNAME, CONFIG_SCHED_LPWORKPRIORITY,
                           CONFIG_SCHED_LPWORKSTACKSIZE,
                           (main_t)work_lpthread,
                           (FAR char * const *)NULL);

      DEBUGASSERT(pid > 0);
      if (pid < 0)
        {
          int errcode = errno;
          DEBUGASSERT(errcode > 0);

          slldbg("kernel_thread %d failed: %d\n", wndx, errcode);
          sched_unlock();
          return -errcode;
        }

      g_lpwork.worker[wndx].pid  = pid;
      g_lpwork.worker[wndx].busy = true;
    }

  sched_unlock();
  return g_lpwork.worker[0].pid;
}
Example #15
0
int pthread_setschedparam(pthread_t thread, int policy, FAR const struct sched_param *param)
{
	int ret;

	svdbg("thread ID=%d policy=%d param=0x%p\n", thread, policy, param);

	/* Let sched_setscheduler do all of the work */

	ret = sched_setscheduler((pid_t)thread, policy, param);
	if (ret != OK) {
		/* If sched_setscheduler() fails, return the errno */

		ret = get_errno();
	}

	return ret;
}
Example #16
0
static inline int spawn_dup2(FAR struct spawn_dup2_file_action_s *action)
{
  int ret;

  /* Perform the dup */

  svdbg("Dup'ing %d->%d\n", action->fd1, action->fd2);

  ret = dup2(action->fd1, action->fd2);
  if (ret < 0)
    {
      int errcode = get_errno();

      sdbg("ERROR: dup2 failed: %d\n", errcode);
      return -errcode;
    }

  return OK;
}
Example #17
0
static bool pthread_notifywaiters(FAR join_t *pjoin)
{
  int ntasks_waiting;
  int status;

  svdbg("pjoin=0x%p\n", pjoin);

  /* Are any tasks waiting for our exit value? */

  status = sem_getvalue(&pjoin->exit_sem, &ntasks_waiting);
  if (status == OK && ntasks_waiting < 0)
    {
      /* Set the data semaphore so that this thread will be
       * awakened when all waiting tasks receive the data
       */

      (void)sem_init(&pjoin->data_sem, 0, (ntasks_waiting+1));

      /* Post the semaphore to restart each thread that is waiting
       * on the semaphore
       */

      do
        {
          status = pthread_givesemaphore(&pjoin->exit_sem);
          if (status == OK)
            {
              status = sem_getvalue(&pjoin->exit_sem, &ntasks_waiting);
            }
        }
      while (ntasks_waiting < 0 && status == OK);

      /* Now wait for all these restarted tasks to obtain the return
       * value.
       */

      (void)pthread_takesemaphore(&pjoin->data_sem);
      return true;
    }
  return false;
}
Example #18
0
int work_usrstart(void)
{
  /* Start a user-mode worker thread for use by applications. */

  svdbg("Starting user-mode worker thread\n");

  g_usrwork[USRWORK].pid = task_create("usrwork",
                                       CONFIG_SCHED_USRWORKPRIORITY,
                                       CONFIG_SCHED_USRWORKSTACKSIZE,
                                       (main_t)work_usrthread,
                                       (FAR char * const *)NULL);

  DEBUGASSERT(g_usrwork[USRWORK].pid > 0);
  if (g_usrwork[USRWORK].pid < 0)
    {
      int errcode = errno;
      DEBUGASSERT(errcode > 0);

      sdbg("task_create failed: %d\n", errcode);
      return -errcode;
    }

  return g_usrwork[USRWORK].pid;
}
Example #19
0
int pthread_mutex_destroy(FAR pthread_mutex_t *mutex)
{
	struct tcb_s *ptcb;
	struct join_s *pjoin = NULL;
	int ret = EINVAL;
	int status;

	svdbg("mutex=0x%p\n", mutex);
	DEBUGASSERT(mutex != NULL);

	if (mutex != NULL) {

		/* Make sure the semaphore is stable while we make the following checks */

		sched_lock();

		/* Is the semaphore available? */

		if (mutex->pid >= 0) {
			DEBUGASSERT(mutex->pid != 0);	/* < 0: available, >0 owned, ==0 error */
		

			/* No.. Verify that the PID still exists.  We may be destroying
			 * the mutex after cancelling a pthread and the mutex may have
			 * been in a bad state owned by the dead pthread.  NOTE: The
			 * following behavior is unspecified for pthread_mutex_destroy()
			 * (see pthread_mutex_consistent()).
			 *
			 * If the holding thread is still valid, then we should be able to
			 * map its PID to the underlying TCB. That is what sched_gettcb()
			 * does.
			 */

			ptcb = sched_gettcb(mutex->pid);
			if (ptcb && ((ptcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)) {
				pjoin = pthread_findjoininfo(ptcb->group, ptcb->pid);
			}

			/* In some cases, pthread_mutex_destroy can be executed on middle of
			 * exiting pthread which holds mutex. The sched_releasepid() updates
			 * g_pidhash list to NULL by calling _exit() from pthread_exit().
			 * But, before calling it, pthread_exit() calls pthread_completejoin().
			 * It gives semaphore of pthread_join before executing _exit().
			 * It might cause user call pthread_mutex_destroy before updating scheduler.
			 * Checking pjoin structure and terminated element helps to check
			 * whether pthread which holds mutex is exiting or not.
			 */

			if (ptcb == NULL || \
			   (((ptcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD) && (pjoin == NULL || pjoin->terminated == true))) {
				/* The thread associated with the PID no longer exists */

				mutex->pid = -1;

				/* Reset the semaphore.  If threads are were on this
				 * semaphore, then this will awakened them and make
				 * destruction of the semaphore impossible here.
				 */

				status = sem_reset((FAR sem_t *)&mutex->sem, 1);
				if (status < 0) {
					ret = -status;
				}

				/* Check if the reset caused some other thread to lock the
				 * mutex.
				 */
				else if (mutex->pid != -1) {
					/* Yes.. then we cannot destroy the mutex now. */
					ret = EBUSY;
				}

				/* Destroy the underlying semaphore */

				else {
					status = sem_destroy((FAR sem_t *)&mutex->sem);
					ret = (status != OK) ? get_errno() : OK;
				}
			} else {
				ret = EBUSY;
			}
		} else {
			/* Destroy the semaphore
			 *
			 * REVISIT:  What if there are threads waiting on the semaphore?
			 * Perhaps this logic should all sem_reset() first?
			 */

			status = sem_destroy((sem_t *)&mutex->sem);
			ret = ((status != OK) ? get_errno() : OK);
		}

		sched_unlock();
	}

	svdbg("Returning %d\n", ret);
	return ret;
}
Example #20
0
pid_t up_vfork(const struct vfork_s *context)
{
  struct tcb_s *parent = (FAR struct tcb_s *)g_readytorun.head;
  struct task_tcb_s *child;
  size_t stacksize;
  uint32_t newsp;
  uint32_t newfp;
  uint32_t stackutil;
  int ret;

  svdbg("vfork context [%p]:\n", context);
  svdbg("  r4:%08x r5:%08x r6:%08x r7:%08x\n",
        context->r4, context->r5, context->r6, context->r7);
  svdbg("  r8:%08x r9:%08x r10:%08x\n",
        context->r8, context->r9, context->r10);
  svdbg("  fp:%08x sp:%08x lr:%08x\n",
        context->fp, context->sp, context->lr);

  /* Allocate and initialize a TCB for the child task. */

  child = task_vforksetup((start_t)(context->lr & ~1));
  if (!child)
    {
      sdbg("ERROR: task_vforksetup failed\n");
      return (pid_t)ERROR;
    }

  svdbg("TCBs: Parent=%p Child=%p\n", parent, child);

  /* Get the size of the parent task's stack.  Due to alignment operations,
   * the adjusted stack size may be smaller than the stack size originally
   * requested.
   */

  stacksize = parent->adj_stack_size + CONFIG_STACK_ALIGNMENT - 1;

  /* Allocate the stack for the TCB */

  ret = up_create_stack((FAR struct tcb_s *)child, stacksize,
                        parent->flags & TCB_FLAG_TTYPE_MASK);
  if (ret != OK)
    {
      sdbg("ERROR: up_create_stack failed: %d\n", ret);
      task_vforkabort(child, -ret);
      return (pid_t)ERROR;
    }

  /* How much of the parent's stack was utilized?  The ARM uses
   * a push-down stack so that the current stack pointer should
   * be lower than the initial, adjusted stack pointer.  The
   * stack usage should be the difference between those two.
   */

  DEBUGASSERT((uint32_t)parent->adj_stack_ptr > context->sp);
  stackutil = (uint32_t)parent->adj_stack_ptr - context->sp;

  svdbg("Parent: stacksize:%d stackutil:%d\n", stacksize, stackutil);

  /* Make some feeble effort to preserve the stack contents.  This is
   * feeble because the stack surely contains invalid pointers and other
   * content that will not work in the child context.  However, if the
   * user follows all of the caveats of vfork() usage, even this feeble
   * effort is overkill.
   */

  newsp = (uint32_t)child->cmn.adj_stack_ptr - stackutil;
  memcpy((void *)newsp, (const void *)context->sp, stackutil);

  /* Was there a frame pointer in place before? */

  if (context->fp <= (uint32_t)parent->adj_stack_ptr &&
      context->fp >= (uint32_t)parent->adj_stack_ptr - stacksize)
    {
      uint32_t frameutil = (uint32_t)parent->adj_stack_ptr - context->fp;
      newfp = (uint32_t)child->cmn.adj_stack_ptr - frameutil;
    }
  else
    {
      newfp = context->fp;
    }

  svdbg("Parent: stack base:%08x SP:%08x FP:%08x\n",
        parent->adj_stack_ptr, context->sp, context->fp);
  svdbg("Child:  stack base:%08x SP:%08x FP:%08x\n",
        child->cmn.adj_stack_ptr, newsp, newfp);

 /* Update the stack pointer, frame pointer, and volatile registers.  When
  * the child TCB was initialized, all of the values were set to zero.
  * up_initial_state() altered a few values, but the return value in R0
  * should be cleared to zero, providing the indication to the newly started
  * child thread.
  */

  child->cmn.xcp.regs[REG_R4]  = context->r4;  /* Volatile register r4 */
  child->cmn.xcp.regs[REG_R5]  = context->r5;  /* Volatile register r5 */
  child->cmn.xcp.regs[REG_R6]  = context->r6;  /* Volatile register r6 */
  child->cmn.xcp.regs[REG_R7]  = context->r7;  /* Volatile register r7 */
  child->cmn.xcp.regs[REG_R8]  = context->r8;  /* Volatile register r8 */
  child->cmn.xcp.regs[REG_R9]  = context->r9;  /* Volatile register r9 */
  child->cmn.xcp.regs[REG_R10] = context->r10; /* Volatile register r10 */
  child->cmn.xcp.regs[REG_FP]  = newfp;        /* Frame pointer */
  child->cmn.xcp.regs[REG_SP]  = newsp;        /* Stack pointer */

#ifdef CONFIG_LIB_SYSCALL
  /* If we got here via a syscall, then we are going to have to setup some
   * syscall return information as well.
   */

  if (parent->xcp.nsyscalls > 0)
    {
      int index;
      for (index = 0; index < parent->xcp.nsyscalls; index++)
        {
          child->cmn.xcp.syscall[index].sysreturn =
            parent->xcp.syscall[index].sysreturn;

          /* REVISIT:  This logic is *not* common. */

#if defined(CONFIG_ARCH_CORTEXA5) || defined(CONFIG_ARCH_CORTEXA8)
#  ifdef CONFIG_BUILD_KERNEL

          child->cmn.xcp.syscall[index].cpsr =
            parent->xcp.syscall[index].cpsr;

#  endif
#elif defined(CONFIG_ARCH_CORTEXM3) || defined(CONFIG_ARCH_CORTEXM4) || \
      defined(CONFIG_ARCH_CORTEXM0) || defined(CONFIG_ARCH_CORTEXM7)

          child->cmn.xcp.syscall[index].excreturn =
            parent->xcp.syscall[index].excreturn;
#else
#  error Missing logic
#endif
        }

      child->cmn.xcp.nsyscalls = parent->xcp.nsyscalls;
    }
#endif

  /* And, finally, start the child task.  On a failure, task_vforkstart()
   * will discard the TCB by calling task_vforkabort().
   */

  return task_vforkstart(child);
}
Example #21
0
void up_sigdeliver(void)
{
  struct tcb_s *rtcb = (struct tcb_s*)g_readytorun.head;
  uint32_t regs[XCPTCONTEXT_REGS];
  sig_deliver_t sigdeliver;

  /* Save the errno.  This must be preserved throughout the signal handling
   * so that the user code final gets the correct errno value (probably
   * EINTR).
   */

  int saved_errno = rtcb->pterrno;

  up_ledon(LED_SIGNAL);

  sdbg("rtcb=%p sigdeliver=%p sigpendactionq.head=%p\n",
        rtcb, rtcb->xcp.sigdeliver, rtcb->sigpendactionq.head);
  ASSERT(rtcb->xcp.sigdeliver != NULL);

  /* Save the real return state on the stack. */

  up_copystate(regs, rtcb->xcp.regs);
  regs[REG_EPC]        = rtcb->xcp.saved_epc;
  regs[REG_STATUS]     = rtcb->xcp.saved_status;

  /* Get a local copy of the sigdeliver function pointer. We do this so that
   * we can nullify the sigdeliver function pointer in the TCB and accept
   * more signal deliveries while processing the current pending signals.
   */

  sigdeliver           = rtcb->xcp.sigdeliver;
  rtcb->xcp.sigdeliver = NULL;

  /* Then restore the task interrupt state */

  irqrestore((irqstate_t)regs[REG_STATUS]);

  /* Deliver the signals */

  sigdeliver(rtcb);

  /* Output any debug messages BEFORE restoring errno (because they may
   * alter errno), then disable interrupts again and restore the original
   * errno that is needed by the user logic (it is probably EINTR).
   */

  svdbg("Resuming EPC: %08x STATUS: %08x\n", regs[REG_EPC], regs[REG_STATUS]);

  (void)irqsave();
  rtcb->pterrno = saved_errno;

  /* Then restore the correct state for this thread of
   * execution.
   */

  up_ledoff(LED_SIGNAL);
  up_fullcontextrestore(regs);

  /* up_fullcontextrestore() should not return but could if the software
   * interrupts are disabled.
   */

  PANIC();
}
Example #22
0
int posix_spawn(FAR pid_t *pid, FAR const char *path,
                FAR const posix_spawn_file_actions_t *file_actions,
                FAR const posix_spawnattr_t *attr,
                FAR char *const argv[], FAR char *const envp[])
#endif
{
  struct sched_param param;
  pid_t proxy;
#ifdef CONFIG_SCHED_WAITPID
  int status;
#endif
  int ret;

  DEBUGASSERT(path);

  svdbg("pid=%p path=%s file_actions=%p attr=%p argv=%p\n",
        pid, path, file_actions, attr, argv);

  /* If there are no file actions to be performed and there is no change to
   * the signal mask, then start the new child task directly from the parent task.
   */

#ifndef CONFIG_DISABLE_SIGNALS
  if ((file_actions == NULL || *file_actions == NULL) &&
      (attr == NULL || (attr->flags & POSIX_SPAWN_SETSIGMASK) == 0))
#else
  if (file_actions ==  NULL || *file_actions == NULL)
#endif
    {
      return posix_spawn_exec(pid, path, attr, argv);
    }

  /* Otherwise, we will have to go through an intermediary/proxy task in order
   * to perform the I/O redirection.  This would be a natural place to fork().
   * However, true fork() behavior requires an MMU and most implementations
   * of vfork() are not capable of these operations.
   *
   * Even without fork(), we can still do the job, but parameter passing is
   * messier.  Unfortunately, there is no (clean) way to pass binary values
   * as a task parameter, so we will use a semaphore-protected global
   * structure.
   */

  /* Get exclusive access to the global parameter structure */

  spawn_semtake(&g_spawn_parmsem);

  /* Populate the parameter structure */

  g_spawn_parms.result       = ENOSYS;
  g_spawn_parms.pid          = pid;
  g_spawn_parms.file_actions = file_actions ? *file_actions : NULL;
  g_spawn_parms.attr         = attr;
  g_spawn_parms.argv         = argv;
  g_spawn_parms.u.posix.path = path;

  /* Get the priority of this (parent) task */

  ret = sched_getparam(0, &param);
  if (ret < 0)
    {
      int errcode = get_errno();

      sdbg("ERROR: sched_getparam failed: %d\n", errcode);
      spawn_semgive(&g_spawn_parmsem);
      return errcode;
    }

  /* Disable pre-emption so that the proxy does not run until waitpid
   * is called.  This is probably unnecessary since the posix_spawn_proxy has
   * the same priority as this thread; it should be schedule behind this
   * task in the ready-to-run list.
   */

#ifdef CONFIG_SCHED_WAITPID
  sched_lock();
#endif

  /* Start the intermediary/proxy task at the same priority as the parent
   * task.
   */

  proxy = kernel_thread("posix_spawn_proxy", param.sched_priority,
                        CONFIG_POSIX_SPAWN_PROXY_STACKSIZE,
                        (main_t)posix_spawn_proxy,
                        (FAR char * const *)NULL);
  if (proxy < 0)
    {
      ret = get_errno();
      sdbg("ERROR: Failed to start posix_spawn_proxy: %d\n", ret);

      goto errout_with_lock;
    }

   /* Wait for the proxy to complete its job */

#ifdef CONFIG_SCHED_WAITPID
   ret = waitpid(proxy, &status, 0);
   if (ret < 0)
     {
       sdbg("ERROR: waitpid() failed: %d\n", errno);
       goto errout_with_lock;
     }
#else
   spawn_semtake(&g_spawn_execsem);
#endif

   /* Get the result and relinquish our access to the parameter structure */

   ret = g_spawn_parms.result;

errout_with_lock:
#ifdef CONFIG_SCHED_WAITPID
  sched_unlock();
#endif
  spawn_semgive(&g_spawn_parmsem);
  return ret;
}
Example #23
0
static int spawn_exec(FAR pid_t *pidp, FAR const char *path,
                      FAR const posix_spawnattr_t *attr,
                      FAR char *const argv[])
{
  struct sched_param param;
  FAR const struct symtab_s *symtab;
  int nsymbols;
  int pid;
  int ret = OK;

  DEBUGASSERT(path);

  /* Get the current symbol table selection */

  exec_getsymtab(&symtab, &nsymbols);

  /* Disable pre-emption so that we can modify the task parameters after
   * we start the new task; the new task will not actually begin execution
   * until we re-enable pre-emption.
   */

  sched_lock();

  /* Start the task */

  pid = exec(path, (FAR const char **)argv, symtab, nsymbols);
  if (pid < 0)
    {
      ret = errno;
      sdbg("ERROR: exec failed: %d\n", ret);
      goto errout;
    }

  /* Return the task ID to the caller */

  if (pid)
    {
      *pidp = pid;
    }

  /* Now set the attributes.  Note that we ignore all of the return values
   * here because we have already successfully started the task.  If we
   * return an error value, then we would also have to stop the task.
   */

  if (attr)
    {
      /* If we are only setting the priority, then call sched_setparm()
       * to set the priority of the of the new task.
       */

      if ((attr->flags & POSIX_SPAWN_SETSCHEDPARAM) != 0)
        {
          /* Get the priority from the attrributes */

          param.sched_priority = attr->priority;

          /* If we are setting *both* the priority and the scheduler,
           * then we will call sched_setscheduler() below.
           */

          if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) == 0)
            {
              svdbg("Setting priority=%d for pid=%d\n",
                    param.sched_priority, pid);

              (void)sched_setparam(pid, &param);
            }
        }

      /* If we are only changing the scheduling policy, then reset
       * the priority to the default value (the same as this thread) in
       * preparation for the sched_setscheduler() call below.
       */

      else if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0)
        {
          (void)sched_getparam(0, &param);
        }

      /* Are we setting the scheduling policy?  If so, use the priority
       * setting determined above.
       */

      if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0)
        {
          svdbg("Setting policy=%d priority=%d for pid=%d\n",
                attr->policy, param.sched_priority, pid);

          (void)sched_setscheduler(pid, attr->policy, &param);
        }
    }

  /* Re-enable pre-emption and return */

errout:
  sched_unlock();
  return ret;
}
Example #24
0
int spawn_execattrs(pid_t pid, FAR const posix_spawnattr_t *attr)
{
  struct sched_param param;

  DEBUGASSERT(attr);

  /* Now set the attributes.  Note that we ignore all of the return values
   * here because we have already successfully started the task.  If we
   * return an error value, then we would also have to stop the task.
   */

  /* If we are only setting the priority, then call sched_setparm()
   * to set the priority of the of the new task.
   */

  if ((attr->flags & POSIX_SPAWN_SETSCHEDPARAM) != 0)
    {
#ifdef CONFIG_SCHED_SPORADIC
      int ret;

      /* Get the current sporadic scheduling parameters.  Those will not be
       * modified.
       */

      ret = sched_getparam(pid, &param);
      if (ret < 0)
        {
          int errcode = get_errno();
          return -errcode;
        }
#endif

      /* Get the priority from the attributes */

      param.sched_priority = attr->priority;

      /* If we are setting *both* the priority and the scheduler,
       * then we will call sched_setscheduler() below.
       */

      if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) == 0)
        {
          svdbg("Setting priority=%d for pid=%d\n",
                param.sched_priority, pid);

          (void)sched_setparam(pid, &param);
        }
    }

  /* If we are only changing the scheduling policy, then reset
   * the priority to the default value (the same as this thread) in
   * preparation for the sched_setscheduler() call below.
   */

  else if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0)
    {
      (void)sched_getparam(0, &param);
    }

  /* Are we setting the scheduling policy?  If so, use the priority
   * setting determined above.
   */

  if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0)
    {
      svdbg("Setting policy=%d priority=%d for pid=%d\n",
            attr->policy, param.sched_priority, pid);

#ifdef CONFIG_SCHED_SPORADIC
      /* But take the sporadic scheduler parameters from the attributes */

      param.sched_ss_low_priority        = (int)attr->low_priority;
      param.sched_ss_max_repl            = (int)attr->max_repl;
      param.sched_ss_repl_period.tv_sec  = attr->repl_period.tv_sec;
      param.sched_ss_repl_period.tv_nsec = attr->repl_period.tv_nsec;
      param.sched_ss_init_budget.tv_sec  = attr->budget.tv_sec;
      param.sched_ss_init_budget.tv_nsec = attr->budget.tv_nsec;
#endif
      (void)sched_setscheduler(pid, attr->policy, &param);
    }

  return OK;
}
Example #25
0
pid_t up_vfork(const struct vfork_s *context)
{
  struct tcb_s *parent = (FAR struct tcb_s *)g_readytorun.head;
  struct task_tcb_s *child;
  size_t stacksize;
  uint32_t newsp;
#if CONFIG_MIPS32_FRAMEPOINTER
  uint32_t newfp;
#endif
  uint32_t stackutil;
  int ret;

  svdbg("s0:%08x s1:%08x s2:%08x s3:%08x s4:%08x\n",
        context->s0, context->s1, context->s2, context->s3, context->s4);
#if CONFIG_MIPS32_FRAMEPOINTER
  svdbg("s5:%08x s6:%08x s7:%08x\n",
        context->s5, context->s6, context->s7);
#ifdef MIPS32_SAVE_GP
  svdbg("fp:%08x sp:%08x ra:%08x gp:%08x\n",
        context->fp, context->sp, context->ra, context->gp);
#else
  svdbg("fp:%08x sp:%08x ra:%08x\n",
        context->fp context->sp, context->ra);
#endif
#else
  svdbg("s5:%08x s6:%08x s7:%08x s8:%08x\n",
        context->s5, context->s6, context->s7, context->s8);
#ifdef MIPS32_SAVE_GP
  svdbg("sp:%08x ra:%08x gp:%08x\n",
        context->sp, context->ra, context->gp);
#else
  svdbg("sp:%08x ra:%08x\n",
        context->sp, context->ra);
#endif
#endif

  /* Allocate and initialize a TCB for the child task. */

  child = task_vforksetup((start_t)context->ra);
  if (!child)
    {
      sdbg("task_vforksetup failed\n");
      return (pid_t)ERROR;
    }

  svdbg("Parent=%p Child=%p\n", parent, child);

  /* Get the size of the parent task's stack.  Due to alignment operations,
   * the adjusted stack size may be smaller than the stack size originally
   * requrested.
   */

  stacksize = parent->adj_stack_size + CONFIG_STACK_ALIGNMENT - 1;

  /* Allocate the stack for the TCB */

  ret = up_create_stack((FAR struct tcb_s *)child, stacksize,
                        parent->flags & TCB_FLAG_TTYPE_MASK);
  if (ret != OK)
    {
      sdbg("up_create_stack failed: %d\n", ret);
      task_vforkabort(child, -ret);
      return (pid_t)ERROR;
    }

  /* How much of the parent's stack was utilized?  The MIPS uses
   * a push-down stack so that the current stack pointer should
   * be lower than the initial, adjusted stack pointer.  The
   * stack usage should be the difference between those two.
   */

  DEBUGASSERT((uint32_t)parent->adj_stack_ptr > context->sp);
  stackutil = (uint32_t)parent->adj_stack_ptr - context->sp;

  svdbg("stacksize:%d stackutil:%d\n", stacksize, stackutil);

  /* Make some feeble effort to perserve the stack contents.  This is
   * feeble because the stack surely contains invalid pointers and other
   * content that will not work in the child context.  However, if the
   * user follows all of the caveats of vfork() usage, even this feeble
   * effort is overkill.
   */

  newsp = (uint32_t)child->cmn.adj_stack_ptr - stackutil;
  memcpy((void *)newsp, (const void *)context->sp, stackutil);

  /* Was there a frame pointer in place before? */

#if CONFIG_MIPS32_FRAMEPOINTER
  if (context->fp <= (uint32_t)parent->adj_stack_ptr &&
      context->fp >= (uint32_t)parent->adj_stack_ptr - stacksize)
    {
      uint32_t frameutil = (uint32_t)parent->adj_stack_ptr - context->fp;
      newfp = (uint32_t)child->cmn.adj_stack_ptr - frameutil;
    }
  else
    {
      newfp = context->fp;
    }

  svdbg("Old stack base:%08x SP:%08x FP:%08x\n",
        parent->adj_stack_ptr, context->sp, context->fp);
  svdbg("New stack base:%08x SP:%08x FP:%08x\n",
        child->cmn.adj_stack_ptr, newsp, newfp);
#else
  svdbg("Old stack base:%08x SP:%08x\n",
        parent->adj_stack_ptr, context->sp);
  svdbg("New stack base:%08x SP:%08x\n",
        child->cmn.adj_stack_ptr, newsp);
#endif

 /* Update the stack pointer, frame pointer, global pointer and saved
  * registers.  When the child TCB was initialized, all of the values
  * were set to zero. up_initial_state() altered a few values, but the
  * return value in v0 should be cleared to zero, providing the
  * indication to the newly started child thread.
  */

  child->cmn.xcp.regs[REG_S0]  = context->s0;  /* Saved register s0 */
  child->cmn.xcp.regs[REG_S1]  = context->s1;  /* Saved register s1 */
  child->cmn.xcp.regs[REG_S2]  = context->s2;  /* Saved register s2 */
  child->cmn.xcp.regs[REG_S3]  = context->s3;  /* Volatile register s3 */
  child->cmn.xcp.regs[REG_S4]  = context->s4;  /* Volatile register s4 */
  child->cmn.xcp.regs[REG_S5]  = context->s5;  /* Volatile register s5 */
  child->cmn.xcp.regs[REG_S6]  = context->s6;  /* Volatile register s6 */
  child->cmn.xcp.regs[REG_S7]  = context->s7;  /* Volatile register s7 */
#if CONFIG_MIPS32_FRAMEPOINTER
  child->cmn.xcp.regs[REG_FP]  = newfp;        /* Frame pointer */
#else
  child->cmn.xcp.regs[REG_S8]  = context->s8;  /* Volatile register s8 */
#endif
  child->cmn.xcp.regs[REG_SP]  = newsp;        /* Stack pointer */
#if MIPS32_SAVE_GP
  child->cmn.xcp.regs[REG_GP]  = newsp;        /* Global pointer */
#endif

  /* And, finally, start the child task.  On a failure, task_vforkstart()
   * will discard the TCB by calling task_vforkabort().
   */

  return task_vforkstart(child);
}
Example #26
0
int os_bringup(void)
{
  int taskid;

  /* Setup up the initial environment for the idle task.  At present, this
   * may consist of only the initial PATH variable.  The PATH variable is
   * (probably) not used by the IDLE task.  However, the environment
   * containing the PATH variable will be inherited by all of the threads
   * created by the IDLE task.
   */

#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)
  (void)setenv("PATH", CONFIG_PATH_INITIAL, 1);
#endif

  /* Start the page fill worker kernel thread that will resolve page faults.
   * This should always be the first thread started because it may have to
   * resolve page faults in other threads
   */

#ifdef CONFIG_PAGING
  svdbg("Starting paging thread\n");

  g_pgworker = KERNEL_THREAD("pgfill", CONFIG_PAGING_DEFPRIO,
                             CONFIG_PAGING_STACKSIZE,
                             (main_t)pg_worker, (FAR char * const *)NULL);
  DEBUGASSERT(g_pgworker > 0);
#endif

  /* Start the worker thread that will serve as the device driver "bottom-
   * half" and will perform misc garbage clean-up.
   */

#ifdef CONFIG_SCHED_WORKQUEUE
#ifdef CONFIG_SCHED_HPWORK

#ifdef CONFIG_SCHED_LPWORK
  svdbg("Starting high-priority kernel worker thread\n");
#else
  svdbg("Starting kernel worker thread\n");
#endif

  g_work[HPWORK].pid = KERNEL_THREAD(HPWORKNAME, CONFIG_SCHED_WORKPRIORITY,
                                     CONFIG_SCHED_WORKSTACKSIZE,
                                     (main_t)work_hpthread, (FAR char * const *)NULL);
  DEBUGASSERT(g_work[HPWORK].pid > 0);

  /* Start a lower priority worker thread for other, non-critical continuation
   * tasks
   */

#ifdef CONFIG_SCHED_LPWORK

  svdbg("Starting low-priority kernel worker thread\n");

  g_work[LPWORK].pid = KERNEL_THREAD(LPWORKNAME, CONFIG_SCHED_LPWORKPRIORITY,
                                     CONFIG_SCHED_LPWORKSTACKSIZE,
                                     (main_t)work_lpthread, (FAR char * const *)NULL);
  DEBUGASSERT(g_work[LPWORK].pid > 0);

#endif /* CONFIG_SCHED_LPWORK */
#endif /* CONFIG_SCHED_HPWORK */

#if defined(CONFIG_NUTTX_KERNEL) && defined(CONFIG_SCHED_USRWORK)
  /* Start the user-space work queue */

  DEBUGASSERT(USERSPACE->work_usrstart != NULL);
  taskid = USERSPACE->work_usrstart();
  DEBUGASSERT(taskid > 0);
#endif

#endif /* CONFIG_SCHED_WORKQUEUE */

  /* Once the operating system has been initialized, the system must be
   * started by spawning the user init thread of execution.  This is the
   * first user-mode thead.
   */

  svdbg("Starting init thread\n");

  /* Perform any last-minute, board-specific initialization, if so
   * configured.
   */

#ifdef CONFIG_BOARD_INITIALIZE
  board_initialize();
#endif

  /* Start the default application.  In a flat build, this is entrypoint
   * is given by the definitions, CONFIG_USER_ENTRYPOINT.  In the kernel
   * build, however, we must get the address of the entrypoint from the
   * header at the beginning of the user-space blob.
   */

#ifdef CONFIG_NUTTX_KERNEL
  DEBUGASSERT(USERSPACE->us_entrypoint != NULL);
  taskid = TASK_CREATE("init", SCHED_PRIORITY_DEFAULT,
                       CONFIG_USERMAIN_STACKSIZE, USERSPACE->us_entrypoint,
                       (FAR char * const *)NULL);
#else
  taskid = TASK_CREATE("init", SCHED_PRIORITY_DEFAULT,
                       CONFIG_USERMAIN_STACKSIZE,
                       (main_t)CONFIG_USER_ENTRYPOINT,
                       (FAR char * const *)NULL);
#endif
  ASSERT(taskid > 0);

  /* We an save a few bytes by discarding the IDLE thread's environment. */

#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)
  (void)clearenv();
#endif

  return OK;
}
Example #27
0
pid_t up_vfork(const struct vfork_s *context)
{
  _TCB *parent = (FAR _TCB *)g_readytorun.head;
  _TCB *child;
  size_t stacksize;
  uint32_t newsp;
  uint32_t newfp;
  uint32_t stackutil;
  int ret;

  svdbg("r4:%08x r5:%08x r6:%08x r7:%08x\n",
        context->r4, context->r5, context->r6, context->r7);
  svdbg("r8:%08x r9:%08x r10:%08x\n",
        context->r8, context->r9, context->r10);
  svdbg("fp:%08x sp:%08x lr:%08x\n",
        context->fp, context->sp, context->lr);

  /* Allocate and initialize a TCB for the child task. */

  child = task_vforksetup((start_t)(context->lr & ~1));
  if (!child)
    {
      sdbg("task_vforksetup failed\n");
      return (pid_t)ERROR;
    }

  svdbg("Parent=%p Child=%p\n", parent, child);

  /* Get the size of the parent task's stack.  Due to alignment operations,
   * the adjusted stack size may be smaller than the stack size originally
   * requrested.
   */

  stacksize = parent->adj_stack_size + CONFIG_STACK_ALIGNMENT - 1;

  /* Allocate the stack for the TCB */

  ret = up_create_stack(child, stacksize);
  if (ret != OK)
    {
      sdbg("up_create_stack failed: %d\n", ret);
      task_vforkabort(child, -ret);
      return (pid_t)ERROR;
    }

  /* How much of the parent's stack was utilized?  The ARM uses
   * a push-down stack so that the current stack pointer should
   * be lower than the initial, adjusted stack pointer.  The
   * stack usage should be the difference between those two.
   */

  DEBUGASSERT((uint32_t)parent->adj_stack_ptr > context->sp);
  stackutil = (uint32_t)parent->adj_stack_ptr - context->sp;

  svdbg("stacksize:%d stackutil:%d\n", stacksize, stackutil); 

  /* Make some feeble effort to perserve the stack contents.  This is
   * feeble because the stack surely contains invalid pointers and other
   * content that will not work in the child context.  However, if the
   * user follows all of the caveats of vfor() usage, even this feeble
   * effort is overkill.
   */

  newsp = (uint32_t)child->adj_stack_ptr - stackutil;
  memcpy((void *)newsp, (const void *)context->sp, stackutil);

  /* Was there a frame pointer in place before? */

  if (context->fp <= (uint32_t)parent->adj_stack_ptr &&
      context->fp >= (uint32_t)parent->adj_stack_ptr - stacksize)
    {
      uint32_t frameutil = (uint32_t)parent->adj_stack_ptr - context->fp;
      newfp = (uint32_t)child->adj_stack_ptr - frameutil;
    }
  else
    {
      newfp = context->fp;
    }

  svdbg("Old stack base:%08x SP:%08x FP:%08x\n",
        parent->adj_stack_ptr, context->sp, context->fp);
  svdbg("New stack base:%08x SP:%08x FP:%08x\n",
        child->adj_stack_ptr, newsp, newfp);

 /* Update the stack pointer, frame pointer, and volatile registers.  When
  * the child TCB was initialized, all of the values were set to zero.
  * up_initial_state() altered a few values, but the return value in R0
  * should be cleared to zero, providing the indication to the newly started
  * child thread.
  */

  child->xcp.regs[REG_R4]  = context->r4;  /* Volatile register r4 */
  child->xcp.regs[REG_R5]  = context->r5;  /* Volatile register r5 */
  child->xcp.regs[REG_R6]  = context->r6;  /* Volatile register r6 */
  child->xcp.regs[REG_R7]  = context->r7;  /* Volatile register r7 */
  child->xcp.regs[REG_R8]  = context->r8;  /* Volatile register r8 */
  child->xcp.regs[REG_R9]  = context->r9;  /* Volatile register r9 */
  child->xcp.regs[REG_R10] = context->r10; /* Volatile register r10 */
  child->xcp.regs[REG_FP]  = newfp;        /* Frame pointer */
  child->xcp.regs[REG_SP]  = newsp;        /* Stack pointer */

  /* And, finally, start the child task.  On a failure, task_vforkstart()
   * will discard the TCB by calling task_vforkabort().
   */

  return task_vforkstart(child);
}
Example #28
0
int pthread_mutex_trylock(FAR pthread_mutex_t *mutex)
{
	int status;
	int ret = EINVAL;

	svdbg("mutex=0x%p\n", mutex);
	DEBUGASSERT(mutex != NULL);

	if (mutex != NULL) {
		int mypid = (int)getpid();

		/* Make sure the semaphore is stable while we make the following
		 * checks.  This all needs to be one atomic action.
		 */

		sched_lock();

		/* Try to get the semaphore. */

		status = pthread_mutex_trytake(mutex);
		if (status == OK) {
			/* If we successfully obtained the semaphore, then indicate
			 * that we own it.
			 */
			mutex->pid = mypid;
#ifdef CONFIG_PTHREAD_MUTEX_TYPES
			if (mutex->type == PTHREAD_MUTEX_RECURSIVE) {
				mutex->nlocks = 1;
			}
#endif
			ret = OK;
		}

		/* pthread_mutex_trytake failed.  Did it fail because the semaphore
		 * was not avaialable?
		 */

		else if (status == EAGAIN) {
#ifdef CONFIG_PTHREAD_MUTEX_TYPES
			/* Check if recursive mutex was locked by the calling thread. */

			if (mutex->type == PTHREAD_MUTEX_RECURSIVE && mutex->pid == mypid) {

				/* Increment the number of locks held and return successfully. */

				if (mutex->nlocks < INT16_MAX) {
					mutex->nlocks++;
					ret = OK;
				} else {
					ret = EOVERFLOW;
				}
			} else
#endif

#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
				/* The calling thread does not hold the semaphore.  The correct
				 * behavior for the 'robust' mutex is to verify that the holder of
				 * the mutex is still valid.  This is protection from the case
				 * where the holder of the mutex has exitted without unlocking it.
				 */

#ifdef CONFIG_PTHREAD_MUTEX_BOTH
#ifdef CONFIG_PTHREAD_MUTEX_TYPES
				/* Check if this NORMAL mutex is robust */

				if (mutex->pid > 0 &&
					((mutex->flags & _PTHREAD_MFLAGS_ROBUST) != 0 ||
					mutex->type != PTHREAD_MUTEX_NORMAL) &&
					sched_gettcb(mutex->pid) == NULL)
#else /* CONFIG_PTHREAD_MUTEX_TYPES */
				/* Check if this NORMAL mutex is robust */

				if (mutex->pid > 0 &&
					(mutex->flags & _PTHREAD_MFLAGS_ROBUST) != 0 &&
					sched_gettcb(mutex->pid) == NULL)
#endif /* CONFIG_PTHREAD_MUTEX_TYPES */
#else /* CONFIG_PTHREAD_MUTEX_ROBUST */
				/* This mutex is always robust, whatever type it is. */
				if (mutex->pid > 0 && sched_gettcb(mutex->pid) == NULL)
#endif
				{
					DEBUGASSERT(mutex->pid != 0);	/* < 0: available, >0 owned, ==0 error */
					DEBUGASSERT((mutex->flags & _PTHREAD_MFLAGS_INCONSISTENT) != 0);

					/* A thread holds the mutex, but there is no such thread.
					 * POSIX requires that the 'robust' mutex return EOWNERDEAD
					 * in this case. It is then the caller's responsibility to
					 * call pthread_mutx_consistent() fo fix the mutex.
					 */

					mutex->flags |= _PTHREAD_MFLAGS_INCONSISTENT;
					ret = EOWNERDEAD;
				}

				/* The mutex is locked by another, active thread */

				else
#endif /* CONFIG_PTHREAD_MUTEX_UNSAFE */
				{
						ret = EBUSY;
				}

			}

			/* Some other, unhandled error occurred */
			else {
				ret = status;
			}

		sched_unlock();
	}

	svdbg("Returning %d\n", ret);
	return ret;
}
Example #29
0
int os_bringup(void)
{
  int init_taskid;

  /* Setup up the initial environment for the idle task.  At present, this
   * may consist of only the initial PATH variable.  The PATH variable is
   * (probably) not used by the IDLE task.  However, the environment
   * containing the PATH variable will be inherited by all of the threads
   * created by the IDLE task.
   */

#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)
  (void)setenv("PATH", CONFIG_PATH_INITIAL, 1);
#endif

  /* Start the page fill worker kernel thread that will resolve page faults.
   * This should always be the first thread started because it may have to
   * resolve page faults in other threads
   */

#ifdef CONFIG_PAGING
  svdbg("Starting paging thread\n");

  g_pgworker = KERNEL_THREAD("pgfill", CONFIG_PAGING_DEFPRIO,
                             CONFIG_PAGING_STACKSIZE,
                             (main_t)pg_worker, (const char **)NULL);
  ASSERT(g_pgworker != ERROR);
#endif

  /* Start the worker thread that will serve as the device driver "bottom-
   * half" and will perform misc garbage clean-up.
   */

#ifdef CONFIG_SCHED_WORKQUEUE
  svdbg("Starting worker thread\n");

  g_work[HPWORK].pid = KERNEL_THREAD("work0", CONFIG_SCHED_WORKPRIORITY,
                                     CONFIG_SCHED_WORKSTACKSIZE,
                                     (main_t)work_hpthread, (const char **)NULL);
  ASSERT(g_work[HPWORK].pid != ERROR);

  /* Start a lower priority worker thread for other, non-critical continuation
   * tasks
   */

#ifdef CONFIG_SCHED_LPWORK
  svdbg("Starting worker thread\n");

  g_work[LPWORK].pid = KERNEL_THREAD("work1", CONFIG_SCHED_LPWORKPRIORITY,
                                     CONFIG_SCHED_LPWORKSTACKSIZE,
                                     (main_t)work_lpthread, (const char **)NULL);
  ASSERT(g_work[LPWORK].pid != ERROR);
#endif
#endif

  /* Once the operating system has been initialized, the system must be
   * started by spawning the user init thread of execution.  This is the
   * first user-mode thead.
   */

  svdbg("Starting init thread\n");

  /* Start the default application at CONFIG_USER_ENTRYPOINT() */

  init_taskid = TASK_CREATE("init", SCHED_PRIORITY_DEFAULT,
                            CONFIG_USERMAIN_STACKSIZE,
                            (main_t)CONFIG_USER_ENTRYPOINT, (const char **)NULL);
  ASSERT(init_taskid != ERROR);

  /* We an save a few bytes by discarding the IDLE thread's environment. */

#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)
  (void)clearenv();
#endif

  return OK;
}
Example #30
0
static inline int mod_loadfile(FAR struct mod_loadinfo_s *loadinfo)
{
  FAR uint8_t *text;
  FAR uint8_t *data;
  FAR uint8_t **pptr;
  int ret;
  int i;

  /* Read each section into memory that is marked SHF_ALLOC + SHT_NOBITS */

  svdbg("Loaded sections:\n");
  text = (FAR uint8_t *)loadinfo->textalloc;
  data = (FAR uint8_t *)loadinfo->datastart;

  for (i = 0; i < loadinfo->ehdr.e_shnum; i++)
    {
      FAR Elf32_Shdr *shdr = &loadinfo->shdr[i];

      /* SHF_ALLOC indicates that the section requires memory during
       * execution */

      if ((shdr->sh_flags & SHF_ALLOC) == 0)
        {
          continue;
        }

      /* SHF_WRITE indicates that the section address space is write-
       * able
       */

      if ((shdr->sh_flags & SHF_WRITE) != 0)
        {
          pptr = &data;
        }
      else
        {
          pptr = &text;
        }

      /* SHT_NOBITS indicates that there is no data in the file for the
       * section.
       */

      if (shdr->sh_type != SHT_NOBITS)
        {
          /* Read the section data from sh_offset to the memory region */

          ret = mod_read(loadinfo, *pptr, shdr->sh_size, shdr->sh_offset);
          if (ret < 0)
            {
              sdbg("ERROR: Failed to read section %d: %d\n", i, ret);
              return ret;
            }
        }

      /* If there is no data in an allocated section, then the allocated
       * section must be cleared.
       */

      else
        {
          memset(*pptr, 0, shdr->sh_size);
        }

      /* Update sh_addr to point to copy in memory */

      svdbg("%d. %08lx->%08lx\n", i,
            (unsigned long)shdr->sh_addr, (unsigned long)*pptr);

      shdr->sh_addr = (uintptr_t)*pptr;

      /* Setup the memory pointer for the next time through the loop */

      *pptr += ELF_ALIGNUP(shdr->sh_size);
    }

  return OK;
}