This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 34412349e9c4323dee221adbbacc3bcd64fc088e
Author: zhangyuan21 <[email protected]>
AuthorDate: Tue Sep 26 10:06:49 2023 +0800

    sched: add smp function call
    
    Support smp function call, calling smp_call_function allows
    a specific core to execute a function. It should be noted
    that there should be no waiting operations in the executed
    function.
    
    Signed-off-by: zhangyuan21 <[email protected]>
---
 include/nuttx/arch.h       |  12 ++
 include/nuttx/sched.h      |  68 +++++++++++
 sched/Kconfig              |   6 +
 sched/sched/CMakeLists.txt |   4 +
 sched/sched/Make.defs      |   4 +
 sched/sched/sched_smp.c    | 294 +++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 388 insertions(+)

diff --git a/include/nuttx/arch.h b/include/nuttx/arch.h
index 723de853df..6fd6a32dd7 100644
--- a/include/nuttx/arch.h
+++ b/include/nuttx/arch.h
@@ -1581,6 +1581,18 @@ void up_secure_irq(int irq, bool secure);
 # define up_secure_irq(i, s)
 #endif
 
+#ifdef CONFIG_SMP_CALL
+/****************************************************************************
+ * Name: up_send_smp_call
+ *
+ * Description:
+ *   Send smp call to target cpu
+ *
+ ****************************************************************************/
+
+void up_send_smp_call(cpu_set_t cpuset);
+#endif
+
 /****************************************************************************
  * Name: up_secure_irq_all
  *
diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h
index f50125e86c..62263896d2 100644
--- a/include/nuttx/sched.h
+++ b/include/nuttx/sched.h
@@ -28,6 +28,7 @@
 #include <nuttx/config.h>
 
 #include <sys/types.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <sched.h>
 #include <signal.h>
@@ -743,6 +744,12 @@ begin_packed_struct struct tcbinfo_s
 
 typedef CODE void (*nxsched_foreach_t)(FAR struct tcb_s *tcb, FAR void *arg);
 
+/* This is the callback type used by nxsched_smp_call() */
+
+#ifdef CONFIG_SMP_CALL
+typedef CODE int (*nxsched_smp_call_t)(FAR void *arg);
+#endif
+
 #endif /* __ASSEMBLY__ */
 
 /****************************************************************************
@@ -1566,6 +1573,67 @@ pid_t nxsched_getppid(void);
 
 size_t nxsched_collect_deadlock(FAR pid_t *pid, size_t count);
 
+#ifdef CONFIG_SMP_CALL
+/****************************************************************************
+ * Name: nxsched_smp_call_handler
+ *
+ * Description:
+ *   SMP function call handler
+ *
+ * Input Parameters:
+ *   irq     - Interrupt id
+ *   context - Regs context before irq
+ *   arg     - Interrupt arg
+ *
+ * Returned Value:
+ *   Result
+ *
+ ****************************************************************************/
+
+int nxsched_smp_call_handler(int irq, FAR void *context,
+                             FAR void *arg);
+
+/****************************************************************************
+ * Name: nxsched_smp_call_single
+ *
+ * Description:
+ *   Call function on single processor
+ *
+ * Input Parameters:
+ *   cpuid - Target cpu id
+ *   func  - Function
+ *   arg   - Function args
+ *   wait  - Wait function callback or not
+ *
+ * Returned Value:
+ *   Result
+ *
+ ****************************************************************************/
+
+int nxsched_smp_call_single(int cpuid, nxsched_smp_call_t func,
+                            FAR void *arg, bool wait);
+
+/****************************************************************************
+ * Name: nxsched_smp_call
+ *
+ * Description:
+ *   Call function on multi processors
+ *
+ * Input Parameters:
+ *   cpuset - Target cpuset
+ *   func   - Function
+ *   arg    - Function args
+ *   wait   - Wait function callback or not
+ *
+ * Returned Value:
+ *   Result
+ *
+ ****************************************************************************/
+
+int nxsched_smp_call(cpu_set_t cpuset, nxsched_smp_call_t func,
+                     FAR void *arg, bool wait);
+#endif
+
 #undef EXTERN
 #if defined(__cplusplus)
 }
diff --git a/sched/Kconfig b/sched/Kconfig
index 0934ff8a9a..5bdeb6de02 100644
--- a/sched/Kconfig
+++ b/sched/Kconfig
@@ -396,6 +396,12 @@ config SMP_DEFAULT_CPUSET
                Set the Default CPU bits. The way to use the unset CPU is to 
call the
                sched_setaffinity function to bind a task to the CPU. bit0 
means CPU0.
 
+config SMP_CALL
+       bool "Support SMP function call"
+       default n
+       ---help---
+               Enable to support SMP function call.
+
 endif # SMP
 
 choice
diff --git a/sched/sched/CMakeLists.txt b/sched/sched/CMakeLists.txt
index 1934cb8621..90ac03e08a 100644
--- a/sched/sched/CMakeLists.txt
+++ b/sched/sched/CMakeLists.txt
@@ -119,4 +119,8 @@ if(CONFIG_SCHED_BACKTRACE)
   list(APPEND SRCS sched_backtrace.c)
 endif()
 
+if(CONFIG_SMP_CALL)
+  list(APPEND SRCS sched_smp.c)
+endif()
+
 target_sources(sched PRIVATE ${SRCS})
diff --git a/sched/sched/Make.defs b/sched/sched/Make.defs
index c69caff613..0abd6c13ab 100644
--- a/sched/sched/Make.defs
+++ b/sched/sched/Make.defs
@@ -96,6 +96,10 @@ ifeq ($(CONFIG_SCHED_BACKTRACE),y)
 CSRCS += sched_backtrace.c
 endif
 
+ifeq ($(CONFIG_SMP_CALL),y)
+CSRCS += sched_smp.c
+endif
+
 # Include sched build support
 
 DEPPATH += --dep-path sched
diff --git a/sched/sched/sched_smp.c b/sched/sched/sched_smp.c
new file mode 100644
index 0000000000..f54532fe61
--- /dev/null
+++ b/sched/sched/sched_smp.c
@@ -0,0 +1,294 @@
+/****************************************************************************
+ * sched/sched/sched_smp.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/queue.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/sched.h>
+#include <nuttx/spinlock.h>
+
+#include "sched/sched.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct smp_call_cookie_s
+{
+  sem_t       sem;
+  int         error;
+};
+
+struct smp_call_data_s
+{
+  sq_entry_t                    node[CONFIG_SMP_NCPUS];
+  nxsched_smp_call_t            func;
+  FAR void                     *arg;
+  FAR struct smp_call_cookie_s *cookie;
+  spinlock_t                    lock;
+  volatile int                  refcount;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static sq_queue_t g_smp_call_queue[CONFIG_SMP_NCPUS];
+static struct smp_call_data_s g_smp_call_data;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxsched_smp_call_add
+ *
+ * Description:
+ *   Add call data to other processors
+ *
+ * Input Parameters:
+ *   cpu        - Target cpu id
+ *   call_data  - Call data
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void nxsched_smp_call_add(int cpu,
+                                 FAR struct smp_call_data_s *call_data)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  sq_addlast(&call_data->node[cpu], &g_smp_call_queue[cpu]);
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxsched_smp_call_handler
+ *
+ * Description:
+ *   SMP function call handler
+ *
+ * Input Parameters:
+ *   irq     - Interrupt id
+ *   context - Regs context before irq
+ *   arg     - Interrupt arg
+ *
+ * Returned Value:
+ *   Result
+ *
+ ****************************************************************************/
+
+int nxsched_smp_call_handler(int irq, FAR void *context,
+                             FAR void *arg)
+{
+  FAR sq_queue_t *call_queue;
+  FAR sq_entry_t *curr;
+  FAR sq_entry_t *next;
+  int cpu = this_cpu();
+
+  irqstate_t flags = enter_critical_section();
+
+  call_queue = &g_smp_call_queue[cpu];
+
+  sq_for_every_safe(call_queue, curr, next)
+    {
+      FAR struct smp_call_data_s *call_data =
+        container_of(curr, struct smp_call_data_s, node[cpu]);
+      int ret;
+
+      sq_rem(&call_data->node[cpu], call_queue);
+
+      leave_critical_section(flags);
+
+      ret = call_data->func(call_data->arg);
+
+      if (call_data->cookie != NULL)
+        {
+          if (ret < 0)
+            {
+              call_data->cookie->error = ret;
+            }
+
+          nxsem_post(&call_data->cookie->sem);
+        }
+
+      if (spin_is_locked(&call_data->lock))
+        {
+          if (--call_data->refcount == 0)
+            {
+              spin_unlock(&call_data->lock);
+            }
+        }
+
+      flags = enter_critical_section();
+    }
+
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nxsched_smp_call_single
+ *
+ * Description:
+ *   Call function on single processor
+ *
+ * Input Parameters:
+ *   cpuid - Target cpu id
+ *   func  - Function
+ *   arg   - Function args
+ *   wait  - Wait function callback or not
+ *
+ * Returned Value:
+ *   Result
+ *
+ ****************************************************************************/
+
+int nxsched_smp_call_single(int cpuid, nxsched_smp_call_t func,
+                            FAR void *arg, bool wait)
+{
+  cpu_set_t cpuset;
+
+  CPU_ZERO(&cpuset);
+  CPU_SET(cpuid, &cpuset);
+  return nxsched_smp_call(cpuset, func, arg, wait);
+}
+
+/****************************************************************************
+ * Name: nxsched_smp_call
+ *
+ * Description:
+ *   Call function on multi processors
+ *
+ * Input Parameters:
+ *   cpuset - Target cpuset
+ *   func   - Function
+ *   arg    - Function args
+ *   wait   - Wait function callback or not
+ *
+ * Returned Value:
+ *   Result
+ *
+ ****************************************************************************/
+
+int nxsched_smp_call(cpu_set_t cpuset, nxsched_smp_call_t func,
+                     FAR void *arg, bool wait)
+{
+  struct smp_call_data_s call_data_stack =
+    {
+      0
+    };
+
+  struct smp_call_cookie_s cookie =
+    {
+      0
+    };
+
+  FAR struct smp_call_data_s *call_data;
+  int remote_cpus = 0;
+  int ret = OK;
+  int i;
+
+  /* Prevent reschedule on another processor */
+
+  sched_lock();
+
+  if (CPU_ISSET(this_cpu(), &cpuset))
+    {
+      ret = func(arg);
+      if (ret < 0)
+        {
+          goto out;
+        }
+
+      CPU_CLR(this_cpu(), &cpuset);
+    }
+
+  /* If waiting is necessary, initialize and wait for the cookie. */
+
+  if (wait)
+    {
+      nxsem_init(&cookie.sem, 0, 0);
+
+      call_data = &call_data_stack;
+      call_data->cookie = &cookie;
+    }
+  else
+    {
+      call_data = &g_smp_call_data;
+      spin_lock(&call_data->lock);
+    }
+
+  call_data->func = func;
+  call_data->arg  = arg;
+
+  for (i = 0; i < CONFIG_SMP_NCPUS; i++)
+    {
+      if (CPU_ISSET(i, &cpuset))
+        {
+          nxsched_smp_call_add(i, call_data);
+          remote_cpus++;
+        }
+    }
+
+  call_data->refcount = remote_cpus;
+
+  if (remote_cpus > 0)
+    {
+      up_send_smp_call(cpuset);
+    }
+
+  if (wait)
+    {
+      for (i = 0; i < remote_cpus; i++)
+        {
+          int wait_ret = nxsem_wait_uninterruptible(&cookie.sem);
+          if (wait_ret < 0)
+            {
+              ret = wait_ret;
+            }
+        }
+
+      if (cookie.error < 0)
+        {
+          ret = cookie.error;
+        }
+
+      nxsem_destroy(&cookie.sem);
+    }
+
+out:
+  sched_unlock();
+  return ret;
+}

Reply via email to