[v16,5/9] eal: implement thread priority management functions

Message ID 1633765318-28356-6-git-send-email-navasile@linux.microsoft.com (mailing list archive)
State Changes Requested, archived
Delegated to: David Marchand
Headers
Series eal: Add EAL API for threading |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Narcisa Ana Maria Vasile Oct. 9, 2021, 7:41 a.m. UTC
From: Narcisa Vasile <navasile@microsoft.com>

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.

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
* priority THREAD_PRIORITY_TIME_CRITICAL

Signed-off-by: Narcisa Vasile <navasile@microsoft.com>
---
 lib/eal/common/rte_thread.c  |  97 ++++++++++++++++++++++++++
 lib/eal/include/rte_thread.h |  34 ++++++++++
 lib/eal/version.map          |   2 +
 lib/eal/windows/rte_thread.c | 127 +++++++++++++++++++++++++++++++++++
 4 files changed, 260 insertions(+)
  

Patch

diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c
index 73b7b3141c..7ab08561a5 100644
--- a/lib/eal/common/rte_thread.c
+++ b/lib/eal/common/rte_thread.c
@@ -50,6 +50,103 @@  rte_thread_get_affinity_by_id(rte_thread_t thread_id,
 		sizeof(*cpuset), cpuset);
 }
 
+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;
+}
+
+int
+rte_thread_get_priority(rte_thread_t thread_id,
+		enum rte_thread_priority *priority)
+{
+	int ret;
+	int policy;
+	struct sched_param param;
+
+	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)
+{
+	int ret;
+	int policy;
+	struct sched_param param;
+
+	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_attr_init(rte_thread_attr_t *attr)
 {
diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h
index 5b100cafda..7077c9ce46 100644
--- a/lib/eal/include/rte_thread.h
+++ b/lib/eal/include/rte_thread.h
@@ -213,6 +213,40 @@  void rte_thread_get_affinity(rte_cpuset_t *cpusetp);
 
 #endif /* RTE_HAS_CPUSET */
 
+/**
+ * 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);
+
+/**
+ * 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.
diff --git a/lib/eal/version.map b/lib/eal/version.map
index 58b9553d59..dcc2c479e9 100644
--- a/lib/eal/version.map
+++ b/lib/eal/version.map
@@ -429,6 +429,8 @@  EXPERIMENTAL {
 	rte_thread_attr_set_priority;
 	rte_thread_get_affinity_by_id;
 	rte_thread_set_affinity_by_id;
+	rte_thread_get_priority;
+	rte_thread_set_priority;
 };
 
 INTERNAL {
diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c
index 0127119f49..5c02a6eaff 100644
--- a/lib/eal/windows/rte_thread.c
+++ b/lib/eal/windows/rte_thread.c
@@ -200,6 +200,133 @@  rte_thread_get_affinity_by_id(rte_thread_t thread_id,
 	return ret;
 }
 
+static int
+thread_map_priority_to_os_value(enum rte_thread_priority eal_pri,
+		int *os_pri, int *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, int 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 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;
+}
+
+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;
+	int 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_attr_init(rte_thread_attr_t *attr)
 {