]> git.droids-corp.org - dpdk.git/commitdiff
eal: get/set thread priority per thread identifier
authorTyler Retzlaff <roretzla@linux.microsoft.com>
Tue, 24 May 2022 11:08:36 +0000 (04:08 -0700)
committerDavid Marchand <david.marchand@redhat.com>
Tue, 7 Jun 2022 11:33:14 +0000 (13:33 +0200)
Add functions for setting and getting the priority of a thread.
Priorities on multiple platforms are similarly determined by a priority
value and a priority class/policy.

Currently in DPDK most threads operate at the OS-default priority level
but there are cases when increasing the priority is useful. For
example, high performance applications may require elevated priority
levels.

For these reasons, EAL will expose two priority levels which are named
suggestively "normal" and "realtime_critical" and are computed as
follows:

  On Linux, the following mapping is created:
    RTE_THREAD_PRIORITY_NORMAL corresponds to
      * policy SCHED_OTHER
      * priority value:   (sched_get_priority_min(SCHED_OTHER) +
   sched_get_priority_max(SCHED_OTHER))/2;
    RTE_THREAD_PRIORITY_REALTIME_CRITICAL corresponds to
      * policy SCHED_RR
      * priority value: sched_get_priority_max(SCHED_RR);

  On Windows, the following mapping is created:
    RTE_THREAD_PRIORITY_NORMAL corresponds to
      * class NORMAL_PRIORITY_CLASS
      * priority THREAD_PRIORITY_NORMAL
    RTE_THREAD_PRIORITY_REALTIME_CRITICAL corresponds to
      * class REALTIME_PRIORITY_CLASS (when running with privileges)
      * class HIGH_PRIORITY_CLASS (when running without privileges)
      * priority THREAD_PRIORITY_TIME_CRITICAL

Note that on Linux the resulting priority value will be 0, in
accordance to the documentation that mention the value should be 0 for
SCHED_OTHER policy.

Signed-off-by: Narcisa Vasile <navasile@linux.microsoft.com>
Signed-off-by: Tyler Retzlaff <roretzla@linux.microsoft.com>
Acked-by: Stephen Hemminger <stephen@networkplumber.org>
lib/eal/include/rte_thread.h
lib/eal/unix/rte_thread.c
lib/eal/version.map
lib/eal/windows/rte_thread.c

index 7888f7a36be99c95f05b34e4b23178e9054ad4ff..9e261bf54412b55f37f72ecf747dafe174016ab0 100644 (file)
@@ -30,6 +30,16 @@ typedef struct {
        uintptr_t opaque_id; /**< thread identifier */
 } rte_thread_t;
 
+/**
+ * Thread priority values.
+ */
+enum rte_thread_priority {
+       RTE_THREAD_PRIORITY_NORMAL            = 0,
+       /**< normal thread priority, the default */
+       RTE_THREAD_PRIORITY_REALTIME_CRITICAL = 1,
+       /**< highest thread priority allowed */
+};
+
 /**
  * TLS key type, an opaque pointer.
  */
@@ -114,6 +124,46 @@ void rte_thread_get_affinity(rte_cpuset_t *cpusetp);
 
 #endif /* RTE_HAS_CPUSET */
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Get the priority of a thread.
+ *
+ * @param thread_id
+ *   Id of the thread for which to get priority.
+ *
+ * @param priority
+ *   Location to store the retrieved priority.
+ *
+ * @return
+ *   On success, return 0.
+ *   On failure, return a positive errno-style error number.
+ */
+__rte_experimental
+int rte_thread_get_priority(rte_thread_t thread_id,
+               enum rte_thread_priority *priority);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Set the priority of a thread.
+ *
+ * @param thread_id
+ *   Id of the thread for which to set priority.
+ *
+ * @param priority
+ *   Priority value to be set.
+ *
+ * @return
+ *   On success, return 0.
+ *   On failure, return a positive errno-style error number.
+ */
+__rte_experimental
+int rte_thread_set_priority(rte_thread_t thread_id,
+               enum rte_thread_priority priority);
+
 /**
  * Create a TLS data key visible to all threads in the process.
  * the created key is later used to get/set a value.
index 9e5fa479f422b9231699175366bbcb444ae5b19b..91265950907077c7a7a5435db5004e49fa7c3fd7 100644 (file)
@@ -16,6 +16,65 @@ struct eal_tls_key {
        pthread_key_t thread_index;
 };
 
+static int
+thread_map_priority_to_os_value(enum rte_thread_priority eal_pri, int *os_pri,
+       int *pol)
+{
+       /* Clear the output parameters. */
+       *os_pri = sched_get_priority_min(SCHED_OTHER) - 1;
+       *pol = -1;
+
+       switch (eal_pri) {
+       case RTE_THREAD_PRIORITY_NORMAL:
+               *pol = SCHED_OTHER;
+
+               /*
+                * Choose the middle of the range to represent the priority
+                * 'normal'.
+                * On Linux, this should be 0, since both
+                * sched_get_priority_min/_max return 0 for SCHED_OTHER.
+                */
+               *os_pri = (sched_get_priority_min(SCHED_OTHER) +
+                       sched_get_priority_max(SCHED_OTHER)) / 2;
+               break;
+       case RTE_THREAD_PRIORITY_REALTIME_CRITICAL:
+               *pol = SCHED_RR;
+               *os_pri = sched_get_priority_max(SCHED_RR);
+               break;
+       default:
+               RTE_LOG(DEBUG, EAL, "The requested priority value is invalid.\n");
+               return EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+thread_map_os_priority_to_eal_priority(int policy, int os_pri,
+       enum rte_thread_priority *eal_pri)
+{
+       switch (policy) {
+       case SCHED_OTHER:
+               if (os_pri == (sched_get_priority_min(SCHED_OTHER) +
+                               sched_get_priority_max(SCHED_OTHER)) / 2) {
+                       *eal_pri = RTE_THREAD_PRIORITY_NORMAL;
+                       return 0;
+               }
+               break;
+       case SCHED_RR:
+               if (os_pri == sched_get_priority_max(SCHED_RR)) {
+                       *eal_pri = RTE_THREAD_PRIORITY_REALTIME_CRITICAL;
+                       return 0;
+               }
+               break;
+       default:
+               RTE_LOG(DEBUG, EAL, "The OS priority value does not map to an EAL-defined priority.\n");
+               return EINVAL;
+       }
+
+       return 0;
+}
+
 rte_thread_t
 rte_thread_self(void)
 {
@@ -28,6 +87,49 @@ rte_thread_self(void)
        return thread_id;
 }
 
+int
+rte_thread_get_priority(rte_thread_t thread_id,
+       enum rte_thread_priority *priority)
+{
+       struct sched_param param;
+       int policy;
+       int ret;
+
+       ret = pthread_getschedparam((pthread_t)thread_id.opaque_id, &policy,
+               &param);
+       if (ret != 0) {
+               RTE_LOG(DEBUG, EAL, "pthread_getschedparam failed\n");
+               goto cleanup;
+       }
+
+       return thread_map_os_priority_to_eal_priority(policy,
+               param.sched_priority, priority);
+
+cleanup:
+       return ret;
+}
+
+int
+rte_thread_set_priority(rte_thread_t thread_id,
+       enum rte_thread_priority priority)
+{
+       struct sched_param param;
+       int policy;
+       int ret;
+
+       /* Realtime priority can cause crashes on non-Windows platforms. */
+       if (priority == RTE_THREAD_PRIORITY_REALTIME_CRITICAL)
+               return ENOTSUP;
+
+       ret = thread_map_priority_to_os_value(priority, &param.sched_priority,
+               &policy);
+       if (ret != 0)
+               return ret;
+
+       return pthread_setschedparam((pthread_t)thread_id.opaque_id, policy,
+               &param);
+}
+
 int
 rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *))
 {
@@ -42,7 +144,7 @@ rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *))
        err = pthread_key_create(&((*key)->thread_index), destructor);
        if (err) {
                RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n",
-                        strerror(err));
+                       strerror(err));
                free(*key);
                rte_errno = ENOEXEC;
                return -1;
@@ -63,7 +165,7 @@ rte_thread_key_delete(rte_thread_key key)
        err = pthread_key_delete(key->thread_index);
        if (err) {
                RTE_LOG(DEBUG, EAL, "pthread_key_delete failed: %s\n",
-                        strerror(err));
+                       strerror(err));
                free(key);
                rte_errno = ENOEXEC;
                return -1;
index d49e30bd042f30c3cd9edc89347e3e6baec61a80..ab27a4bb44f3deb3212b924df5fe767f8e03c2f0 100644 (file)
@@ -423,8 +423,10 @@ EXPERIMENTAL {
 
        # added in 22.07
        rte_thread_get_affinity_by_id;
+       rte_thread_get_priority;
        rte_thread_self;
        rte_thread_set_affinity_by_id;
+       rte_thread_set_priority;
 };
 
 INTERNAL {
index a616703cf167f7a814904f145fd5b5daf9fe5ab7..077152568fcf4ff7e97afc0b53567785992822b7 100644 (file)
@@ -61,6 +61,59 @@ thread_log_last_error(const char *message)
        return thread_translate_win32_error(error);
 }
 
+static int
+thread_map_priority_to_os_value(enum rte_thread_priority eal_pri, int *os_pri,
+       DWORD *pri_class)
+{
+       /* Clear the output parameters. */
+       *os_pri = -1;
+       *pri_class = -1;
+
+       switch (eal_pri) {
+       case RTE_THREAD_PRIORITY_NORMAL:
+               *pri_class = NORMAL_PRIORITY_CLASS;
+               *os_pri = THREAD_PRIORITY_NORMAL;
+               break;
+       case RTE_THREAD_PRIORITY_REALTIME_CRITICAL:
+               *pri_class = REALTIME_PRIORITY_CLASS;
+               *os_pri = THREAD_PRIORITY_TIME_CRITICAL;
+               break;
+       default:
+               RTE_LOG(DEBUG, EAL, "The requested priority value is invalid.\n");
+               return EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+thread_map_os_priority_to_eal_value(int os_pri, DWORD pri_class,
+       enum rte_thread_priority *eal_pri)
+{
+       switch (pri_class) {
+       case NORMAL_PRIORITY_CLASS:
+               if (os_pri == THREAD_PRIORITY_NORMAL) {
+                       *eal_pri = RTE_THREAD_PRIORITY_NORMAL;
+                       return 0;
+               }
+               break;
+       case HIGH_PRIORITY_CLASS:
+               RTE_LOG(WARNING, EAL, "The OS priority class is high not real-time.\n");
+               /* FALLTHROUGH */
+       case REALTIME_PRIORITY_CLASS:
+               if (os_pri == THREAD_PRIORITY_TIME_CRITICAL) {
+                       *eal_pri = RTE_THREAD_PRIORITY_REALTIME_CRITICAL;
+                       return 0;
+               }
+               break;
+       default:
+               RTE_LOG(DEBUG, EAL, "The OS priority value does not map to an EAL-defined priority.\n");
+               return EINVAL;
+       }
+
+       return 0;
+}
+
 rte_thread_t
 rte_thread_self(void)
 {
@@ -71,6 +124,83 @@ rte_thread_self(void)
        return thread_id;
 }
 
+int
+rte_thread_get_priority(rte_thread_t thread_id,
+       enum rte_thread_priority *priority)
+{
+       HANDLE thread_handle = NULL;
+       DWORD pri_class;
+       int os_pri;
+       int ret;
+
+       pri_class = GetPriorityClass(GetCurrentProcess());
+       if (pri_class == 0) {
+               ret = thread_log_last_error("GetPriorityClass()");
+               goto cleanup;
+       }
+
+       thread_handle = OpenThread(THREAD_SET_INFORMATION |
+               THREAD_QUERY_INFORMATION, FALSE, thread_id.opaque_id);
+       if (thread_handle == NULL) {
+               ret = thread_log_last_error("OpenThread()");
+               goto cleanup;
+       }
+
+       os_pri = GetThreadPriority(thread_handle);
+       if (os_pri == THREAD_PRIORITY_ERROR_RETURN) {
+               ret = thread_log_last_error("GetThreadPriority()");
+               goto cleanup;
+       }
+
+       ret = thread_map_os_priority_to_eal_value(os_pri, pri_class, priority);
+       if (ret != 0)
+               goto cleanup;
+
+cleanup:
+       if (thread_handle != NULL)
+               CloseHandle(thread_handle);
+
+       return ret;
+}
+
+int
+rte_thread_set_priority(rte_thread_t thread_id,
+                       enum rte_thread_priority priority)
+{
+       HANDLE thread_handle;
+       DWORD priority_class;
+       int os_priority;
+       int ret = 0;
+
+       thread_handle = OpenThread(THREAD_SET_INFORMATION |
+               THREAD_QUERY_INFORMATION, FALSE, thread_id.opaque_id);
+       if (thread_handle == NULL) {
+               ret = thread_log_last_error("OpenThread()");
+               goto cleanup;
+       }
+
+       ret = thread_map_priority_to_os_value(priority, &os_priority,
+               &priority_class);
+       if (ret != 0)
+               goto cleanup;
+
+       if (!SetPriorityClass(GetCurrentProcess(), priority_class)) {
+               ret = thread_log_last_error("SetPriorityClass()");
+               goto cleanup;
+       }
+
+       if (!SetThreadPriority(thread_handle, os_priority)) {
+               ret = thread_log_last_error("SetThreadPriority()");
+               goto cleanup;
+       }
+
+cleanup:
+       if (thread_handle != NULL)
+               CloseHandle(thread_handle);
+
+       return ret;
+}
+
 int
 rte_thread_key_create(rte_thread_key *key,
                __rte_unused void (*destructor)(void *))