From fc745767d4a04c15d829c359653fa5b75ef21584 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 14 Jun 2019 08:03:37 -0700 Subject: [PATCH] governor: add daemon API and helpers to set CPU governor --- src/libsysprof/sysprof-helpers.c | 66 +++++++++++++++++ src/libsysprof/sysprof-helpers.h | 9 +++ src/org.gnome.Sysprof3.Service.xml | 12 ++++ src/sysprofd/ipc-service-impl.c | 112 +++++++++++++++++++++++++++++ 4 files changed, 199 insertions(+) diff --git a/src/libsysprof/sysprof-helpers.c b/src/libsysprof/sysprof-helpers.c index a98245ba..4ba3dd0a 100644 --- a/src/libsysprof/sysprof-helpers.c +++ b/src/libsysprof/sysprof-helpers.c @@ -671,3 +671,69 @@ sysprof_helpers_get_process_info_finish (SysprofHelpers *self, return FALSE; } + +static void +sysprof_helpers_set_governor_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + IpcService *proxy = (IpcService *)object; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = user_data; + gchar *old_governor = NULL; + + g_assert (IPC_IS_SERVICE (proxy)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (!ipc_service_call_set_governor_finish (proxy, &old_governor, result, &error)) + g_task_return_error (task, g_steal_pointer (&error)); + else + g_task_return_pointer (task, old_governor, g_free); +} + +void +sysprof_helpers_set_governor_async (SysprofHelpers *self, + const gchar *governor, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_HELPERS (self)); + g_return_if_fail (governor != NULL); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_helpers_set_governor_async); + + if (fail_if_no_proxy (self, task)) + return; + + ipc_service_call_set_governor (self->proxy, + governor, + cancellable, + sysprof_helpers_set_governor_cb, + g_steal_pointer (&task)); +} + +gboolean +sysprof_helpers_set_governor_finish (SysprofHelpers *self, + GAsyncResult *result, + gchar **old_governer, + GError **error) +{ + g_autofree gchar *ret = NULL; + + g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE); + g_return_val_if_fail (G_IS_TASK (result), FALSE); + + if ((ret = g_task_propagate_pointer (G_TASK (result), error))) + { + if (old_governer != NULL) + *old_governer = g_steal_pointer (&ret); + return TRUE; + } + + return FALSE; +} diff --git a/src/libsysprof/sysprof-helpers.h b/src/libsysprof/sysprof-helpers.h index 3cce84af..9f50a8f7 100644 --- a/src/libsysprof/sysprof-helpers.h +++ b/src/libsysprof/sysprof-helpers.h @@ -99,5 +99,14 @@ gboolean sysprof_helpers_perf_event_open (SysprofHelpers gint *out_fd, GError **error); #endif +void sysprof_helpers_set_governor_async (SysprofHelpers *self, + const gchar *governor, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean sysprof_helpers_set_governor_finish (SysprofHelpers *self, + GAsyncResult *result, + gchar **old_governor, + GError **error); G_END_DECLS diff --git a/src/org.gnome.Sysprof3.Service.xml b/src/org.gnome.Sysprof3.Service.xml index 6b576791..70a83ae5 100644 --- a/src/org.gnome.Sysprof3.Service.xml +++ b/src/org.gnome.Sysprof3.Service.xml @@ -92,5 +92,17 @@ + + + + + + diff --git a/src/sysprofd/ipc-service-impl.c b/src/sysprofd/ipc-service-impl.c index 3b668abb..db2d0f1a 100644 --- a/src/sysprofd/ipc-service-impl.c +++ b/src/sysprofd/ipc-service-impl.c @@ -46,6 +46,54 @@ enum { static guint signals [N_SIGNALS]; +static gboolean +file_set_contents_no_backup (const gchar *path, + const gchar *contents, + gssize len, + GError **error) +{ + int fd; + + g_return_val_if_fail (path != NULL, FALSE); + + if (contents == NULL) + contents = ""; + + if (len < 0) + len = strlen (contents); + + /* This is only for setting files in /sys, which need to be a single + * write anyway, so don't try to incrementally write at all. + */ + + fd = open (path, O_WRONLY, 0644); + + if (fd == -1) + { + int errsv = errno; + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "%s", g_strerror (errsv)); + return FALSE; + } + + if (write (fd, contents, len) != len) + { + int errsv = errno; + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "%s", g_strerror (errsv)); + close (fd); + return FALSE; + } + + close (fd); + + return TRUE; +} + static gboolean ipc_service_impl_handle_list_processes (IpcService *service, GDBusMethodInvocation *invocation) @@ -263,6 +311,69 @@ ipc_service_impl_handle_get_process_info (IpcService *service, return TRUE; } +static gboolean +ipc_service_impl_handle_set_governor (IpcService *service, + GDBusMethodInvocation *invocation, + const gchar *governor) +{ + g_autoptr(GVariant) res = NULL; + g_autofree gchar *available = NULL; + g_autofree gchar *previous = NULL; + gboolean had_error = FALSE; + guint n_procs; + + g_assert (IPC_IS_SERVICE (service)); + g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation)); + g_assert (governor != NULL); + + if (!g_file_get_contents ("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors", &available, NULL, NULL)) + { + g_warning ("Failed to discover available governors"); + had_error = TRUE; + goto finish; + } + + if (strstr (available, governor) == NULL) + { + /* No such governor */ + g_warning ("No such governor \"%s\"", governor); + had_error = TRUE; + goto finish; + } + + if (g_file_get_contents ("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", &previous, NULL, NULL)) + g_strstrip (previous); + else + previous = g_strdup ("unknown"); + + n_procs = g_get_num_processors (); + + for (guint i = 0; i < n_procs; i++) + { + g_autofree gchar *path = g_strdup_printf ("/sys/devices/system/cpu/cpu%u/cpufreq/scaling_governor", i); + g_autoptr(GError) error = NULL; + + if (!file_set_contents_no_backup (path, governor, -1, &error)) + { + g_warning ("Failed to set governor on CPU %u: %s", i, error->message); + had_error = TRUE; + } + } + +finish: + if (had_error) + g_dbus_method_invocation_return_error (g_steal_pointer (&invocation), + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Failed to set governor"); + else + ipc_service_complete_set_governor (service, + g_steal_pointer (&invocation), + previous); + + return TRUE; +} + static void init_service_iface (IpcServiceIface *iface) { @@ -271,6 +382,7 @@ init_service_iface (IpcServiceIface *iface) iface->handle_get_proc_fd = ipc_service_impl_handle_get_proc_fd; iface->handle_perf_event_open = ipc_service_impl_handle_perf_event_open; iface->handle_get_process_info = ipc_service_impl_handle_get_process_info; + iface->handle_set_governor = ipc_service_impl_handle_set_governor; } G_DEFINE_TYPE_WITH_CODE (IpcServiceImpl, ipc_service_impl, IPC_TYPE_SERVICE_SKELETON,