+#include <string.h>
+#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include <pthread.h>
+#include <sys/un.h>
#include <sys/time.h>
#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <systemd/sd-daemon.h>
#include "module.h"
#include "macro.h"
#include "notifier.h"
#include "resourced.h"
#include "cpu-common.h"
+#include "fd-handler.h"
#include "proc-common.h"
static GHashTable *tid_table;
static void cpu_boosting_destroy_request(struct cpu_boosting_input *input);
+#define SOCK_PATH "/run/.resourced.socket"
+
#define CPU_BOOSTING_WORKER_IS_ACTIVE g_atomic_int_get(&worker.active)
#define CPU_BOOSTING_WORKER_ACTIVATE g_atomic_int_set(&worker.active, 1)
#define CPU_BOOSTING_WORKER_DEACTIVATE g_atomic_int_set(&worker.active, 0)
-#define CPU_BOOSTING_SET_REQUEST(_input, _command, _level, _timeout_msec) \
-{ \
- (_input)->command = _command; (_input)->level = _level; \
- (_input)->timeout_msec = _timeout_msec; \
+#define CPU_BOOSTING_SET_REQUEST(_input, _command, _level, _timeout_msec, _pid) \
+{ \
+ (_input)->client_input.command = _command; \
+ (_input)->client_input.timeout_msec = _timeout_msec; \
+ (_input)->client_input.level = _level; \
+ (_input)->client_input.pid = _pid; \
}
#define CPU_BOOSTING_ENQUEUE_REQUEST(_input) \
do { \
if (CPU_BOOSTING_WORKER_IS_ACTIVE) \
g_async_queue_push(worker.queue, _input); \
- else \
+ else { \
+ _W("[CPU-BOOSTING] Cpu boosting is not active"); \
cpu_boosting_destroy_request(_input); \
+ } \
} while (0)
static void cpu_boosting_destroy_request(struct cpu_boosting_input *input)
if (input == NULL)
return;
- if (input->tid_list)
- free(input->tid_list);
+ if (input->client_input.pid.tid)
+ free(input->client_input.pid.tid);
if (input->gsource_id)
g_free(input->gsource_id);
g_slice_free(struct cpu_boosting_input, input);
}
-static struct cpu_boosting_input *cpu_boosting_new_request(resource_pid_t pid)
+static struct cpu_boosting_input *cpu_boosting_new_request(void)
{
struct cpu_boosting_input *input = g_slice_new0(struct cpu_boosting_input);
if (input == NULL) {
return NULL;
}
-#define CPU_BOOSTING_TEMP_TID_LIST_SIZE 50
- if (pid.pid > 0) {
- int r;
- int tid;
- int tid_list[CPU_BOOSTING_TEMP_TID_LIST_SIZE];
- int tid_count = 0;
- _cleanup_free_ char *buf = NULL;
- DIR *dir;
- struct dirent *dirent;
- r = asprintf(&buf, "/proc/%d/task", pid.pid);
- if (r < 0)
- goto dealloc_input;
-
- dir = opendir(buf);
- if (!dir)
- goto dealloc_input;
- while ((dirent = readdir(dir))) {
- const char *id = dirent->d_name;
- if(!isdigit(*id))
- continue;
-
- tid = atoi(id);
- if (tid > 0) {
- if (tid_count < CPU_BOOSTING_TEMP_TID_LIST_SIZE)
- tid_list[tid_count++] = tid;
- else {
- _E("[CPU_BOOSTING] Temp tid list size is smaller than the number of threads of pid = %d", pid.pid);
- closedir(dir);
- goto dealloc_input;
- }
- }
- }
- closedir(dir);
-
- input->tid_list = (int *)calloc(tid_count, sizeof(int));
- if (input->tid_list == NULL)
- goto dealloc_input;
-
- input->tid_count = tid_count;
- for (int i = 0; i < tid_count; i++)
- input->tid_list[i] = tid_list[i];
- }
- else {
- input->tid_list = (int *)calloc(pid.tid_count, sizeof(int));
- if (input->tid_list == NULL)
- goto dealloc_input;
-
- input->tid_count = pid.tid_count;
- for (int i = 0; i < pid.tid_count; i++)
- input->tid_list[i] = pid.tid[i];
- }
-
return input;
-
-dealloc_input:
- g_slice_free(struct cpu_boosting_input, input);
- return NULL;
}
static bool load_cpu_boosting_config(cpu_boosting_level_e level)
}
}
+static void cpu_boosting_send_reply(int sock, cpu_boosting_output_t *output)
+{
+ int byte;
+
+ if (sock == 0)
+ return; /* cpu_boosting by conf */
+ else if (sock < 0) {
+ _E("[CPU-BOOSTING] socket cannot be negative");
+ return;
+ }
+
+ if (output->level.tid_level && output->level.tid_count > 0) {
+ /* Send a header first */
+ byte = send(sock, (const void *)output, sizeof(*output), 0);
+ if (byte != sizeof(*output)) {
+ _E("[CPU-BOOSTING] Server output size is %u, but sent size is %d",
+ (unsigned int)sizeof(*output), byte);
+ return;
+ }
+
+ /* Check body size */
+ byte = send(sock, (const void *)output->level.tid_level,
+ output->level.tid_count * sizeof(int), 0);
+ if (byte != output->level.tid_count * sizeof(int)) {
+ _E("[CPU-BOOSTING] Server output size is %u, but sent size is %d",
+ output->level.tid_count * (unsigned int)sizeof(int), byte);
+ return;
+ }
+ }
+ else {
+ /* Send a header */
+ byte = send(sock, (const void *)&output->success, sizeof(output->success), 0);
+ if (byte != sizeof(output->success)) {
+ _E("[CPU-BOOSTING] Server output size is %u, but sent size is %d",
+ (unsigned int)sizeof(output->success), byte);
+ return;
+ }
+ }
+}
+
+static int cpu_boosting_enqueue_by_socket(int sock)
+{
+ int ret = RESOURCED_ERROR_NONE;
+ int byte;
+ struct cpu_boosting_input *input;
+ cpu_boosting_output_t output;
+
+ input = cpu_boosting_new_request();
+ if (input == NULL) {
+ ret = RESOURCED_ERROR_OUT_OF_MEMORY;
+ goto destroy_input;
+ }
+
+ /* Get a header from the client */
+ byte = recv(sock, (void *)&input->client_input, sizeof(input->client_input), 0);
+ if (byte != sizeof(input->client_input)) {
+ ret = RESOURCED_ERROR_FAIL;
+ if (byte == 0)
+ goto destroy_input; /* connection is closed */
+
+ _E("[CPU-BOOSTING] error is based on %s", strerror(errno));
+ _E("[CPU-BOOSTING] client input size is %u, but received size is %d",
+ (unsigned int)sizeof(input->client_input), byte);
+ input->client_input.pid.tid = NULL;
+ goto destroy_input;
+ }
+
+ /* Check body size */
+ input->client_input.pid.tid = NULL;
+ if (input->client_input.body_size > 0 &&
+ input->client_input.pid.tid_count > 0) {
+ int *tid_list;
+ int tid_count = input->client_input.pid.tid_count;
+
+ tid_list = (int *)calloc(tid_count, sizeof(int));
+ if (tid_list == NULL) {
+ _E("[CPU-BOOSTING] Failed to allocate memory");
+ ret = RESOURCED_ERROR_OUT_OF_MEMORY;
+ goto destroy_input;
+ }
+ else
+ input->client_input.pid.tid = tid_list;
+
+ /* Get a body from the client */
+ byte = recv(sock, (void *)tid_list, tid_count * sizeof(int), 0);
+ if (byte != tid_count * sizeof(int)) {
+ ret = RESOURCED_ERROR_FAIL;
+ if (byte == 0)
+ goto destroy_input; /* connection is closed */
+
+ _E("[CPU-BOOSTING] error is based on %s", strerror(errno));
+ _E("[CPU-BOOSTING] client input size is %u, but received size is %d",
+ tid_count * (unsigned int)sizeof(int), byte);
+ goto destroy_input;
+ }
+ }
+
+ if (input->client_input.command == CPU_BOOSTING_COMMAND_SET ||
+ input->client_input.command == CPU_BOOSTING_COMMAND_CLEAR) {
+ output.success = true;
+ output.level.tid_level = NULL;
+ output.level.tid_count = 0;
+ cpu_boosting_send_reply(sock, &output);
+ }
+
+ input->sock = sock; /* For a reply */
+ CPU_BOOSTING_ENQUEUE_REQUEST(input);
+ return RESOURCED_ERROR_NONE;
+
+destroy_input:
+ cpu_boosting_destroy_request(input);
+
+ return ret;
+}
+
/* Called by resourced main thread */
static int cpu_boosting_enqueue_by_conf(void *data)
{
return RESOURCED_ERROR_FAIL;
}
+ pid.tid = NULL;
+ pid.tid_count = 0;
pid.pid = ps->pid;
if (pid.pid <= 0) {
_E("[CPU-BOOSTING] pid should be larger than 0");
return RESOURCED_ERROR_NONE;
}
- input = cpu_boosting_new_request(pid);
- if (input == NULL) {
- _E("[CPU-BOOSTING] Failed to allocate cpu boosting input");
+ input = cpu_boosting_new_request();
+ if (input == NULL)
return RESOURCED_ERROR_FAIL;
- }
- CPU_BOOSTING_SET_REQUEST(input, CPU_BOOSTING_COMMAND_SET, cpu_boosting_level, -1);
+ CPU_BOOSTING_SET_REQUEST(input, CPU_BOOSTING_COMMAND_SET, cpu_boosting_level, -1, pid);
CPU_BOOSTING_ENQUEUE_REQUEST(input);
return RESOURCED_ERROR_NONE;
goto timer_out;
}
- int tid_count = input->tid_count;
- int *tid_list = input->tid_list;
+ int tid_count = input->client_input.pid.tid_count;
+ int *tid_list = input->client_input.pid.tid;
cpu_boosting_level_e cpu_boosting_level = CPU_BOOSTING_LEVEL_NONE;
for (int i = 0; i < tid_count; i++) {
return G_SOURCE_REMOVE;
}
-static void cpu_boosting_set_or_clear(struct cpu_boosting_input *input,
- struct cpu_boosting_output *output)
+static void cpu_boosting_set_or_clear(struct cpu_boosting_input *input)
{
+ int ret;
bool need_to_clear = false;
int fail_cnt = 0;
- int tid_count = input->tid_count;
- int *tid_list = input->tid_list;
- int timeout_msec = input->timeout_msec;
- cpu_boosting_level_e cpu_boosting_level = input->level;
+ int pid = input->client_input.pid.pid;
+ int tid_count = input->client_input.pid.tid_count;
+ int *tid_list = input->client_input.pid.tid;
+ int timeout_msec = input->client_input.timeout_msec;
+ cpu_boosting_level_e cpu_boosting_level = input->client_input.level;
- for (int i = 0; i < tid_count; i++) {
- if (tid_list[i] > 0) {
- g_hash_table_remove(tid_table, (gpointer)&tid_list[i]);
- if (sched_setattr(tid_list[i], &cpu_boosting_attr[cpu_boosting_level], 0) < 0) {
- _E("[CPU-BOOSTING] Failed to boost cpu of (tid = %d) with (level = %d)",
- tid_list[i], cpu_boosting_level);
- fail_cnt++;
+ if (pid > 0) {
+ int tid;
+ DIR *dir;
+ struct dirent *dirent;
+ _cleanup_free_ char *buf = NULL;
+ ret = asprintf(&buf, "/proc/%d/task", pid);
+ if (ret < 0) {
+ fail_cnt++;
+ _E("[CPU-BOOSTING] There is no (dir = %s)", buf);
+ goto output_update;
+ }
+
+ dir = opendir(buf);
+ if (!dir) {
+ fail_cnt++;
+ _E("[CPU-BOOSTING] Failed to open (dir = %s)", buf);
+ goto output_update;
+ }
+
+ while ((dirent = readdir(dir))) {
+ const char *id = dirent->d_name;
+ if(!isdigit(*id))
continue;
+
+ tid = atoi(id);
+ if (tid > 0) {
+ g_hash_table_remove(tid_table, (gpointer)&tid);
+ if (sched_setattr(tid, &cpu_boosting_attr[cpu_boosting_level], 0) < 0) {
+ _E("[CPU-BOOSTING] Failed to boost cpu of (tid = %d) with (level = %d)",
+ tid, cpu_boosting_level);
+ fail_cnt++;
+ continue;
+ }
+ need_to_clear = true;
}
- need_to_clear = true;
+ else
+ _E("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid);
}
- else {
- _E("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid_list[i]);
- fail_cnt++;
+ closedir(dir);
+ }
+ else {
+ for (int i = 0; i < tid_count; i++) {
+ if (tid_list[i] > 0) {
+ g_hash_table_remove(tid_table, (gpointer)&tid_list[i]);
+ if (sched_setattr(tid_list[i], &cpu_boosting_attr[cpu_boosting_level], 0) < 0) {
+ _E("[CPU-BOOSTING] Failed to boost cpu of (tid = %d) with (level = %d)",
+ tid_list[i], cpu_boosting_level);
+ fail_cnt++;
+ continue;
+ }
+ need_to_clear = true;
+ }
+ else {
+ _E("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid_list[i]);
+ }
}
}
+output_update:
if (fail_cnt > 0)
- output->success = false;
- else
- output->success = true;
+ _E("[CPU-BOOSTING] Boosting fail count = %d", fail_cnt);
+
+
+ /*
+ * If timeout value is larger than 0 and at least one boosting is succeed,
+ * then set the timer to restore
+ */
if (timeout_msec > 0 && need_to_clear) {
input->gsource_id = g_new(guint, 1);
*(input->gsource_id) = g_timeout_add(timeout_msec, cpu_boosting_timeout, input);
(gpointer)input->gsource_id);
}
}
- else
+ else {
cpu_boosting_destroy_request(input);
+ }
}
static void cpu_boosting_get(struct cpu_boosting_input *input,
- struct cpu_boosting_output *output)
+ cpu_boosting_output_t *output)
{
}
while (1) {
struct cpu_boosting_input *input;
- struct cpu_boosting_output output;
+ cpu_boosting_output_t output;
if (!CPU_BOOSTING_WORKER_IS_ACTIVE)
break;
* Whether current boosting level is supported or not should be checked before
* calling CPU_BOOSTING_ENQUEUE_REQUEST. That is the cpu boosting thread ignores it
*/
- switch (input->command) {
+ switch (input->client_input.command) {
case CPU_BOOSTING_COMMAND_SET:
case CPU_BOOSTING_COMMAND_CLEAR:
- cpu_boosting_set_or_clear(input, &output);
+ cpu_boosting_set_or_clear(input);
break;
case CPU_BOOSTING_COMMAND_GET:
cpu_boosting_get(input, &output);
_E("[CPU-BOOSTING] Unknwon cpu boosting command");
cpu_boosting_destroy_request(input);
}
-
}
g_async_queue_unref(worker.queue);
g_async_queue_unref(worker.queue);
}
+static int cpu_boosting_init_socket(void)
+{
+ /* Create a server socket */
+ int ret;
+ int sock = -1;
+ socklen_t size;
+ struct sockaddr_un sockaddr = {0, };
+ int n = sd_listen_fds(0);
+ if (n > 0) {
+ for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
+ if (sd_is_socket_unix(fd, SOCK_STREAM, 1, SOCK_PATH, 0) > 0) {
+ sock = fd;
+ break;
+ }
+ }
+ }
+
+ if (sock < 0) {
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ _E("[CPU-BOOSTING] Failed to allocate a socket");
+ goto return_sock;
+ }
+
+ sockaddr.sun_family = AF_UNIX;
+ strncpy(sockaddr.sun_path, (const char *)SOCK_PATH, strlen(SOCK_PATH)+1);
+ size = sizeof(sockaddr);
+ unlink(SOCK_PATH);
+
+ ret = bind(sock, (struct sockaddr *)&sockaddr, size);
+ if (ret < 0) {
+ _E("[CPU-BOOSTING] Failed to bind socket");
+ goto close_sock;
+ }
+
+ ret = listen(sock, SOMAXCONN);
+ if (ret < 0) {
+ _E("[CPU-BOOSTING] Failed to listen socket");
+ goto close_sock;
+ }
+ }
+
+return_sock:
+ return sock;
+
+close_sock:
+ close(sock);
+ return 0;
+}
+
+/* Client asks send() to the resourced, so call recv() */
+bool cpu_boosting_recv_from_client(int fd, void *data)
+{
+ int client_sock = fd;
+
+ if (client_sock <= 0) {
+ _E("[CPU-BOOSTING] client socket should be larger than 0");
+ goto remove_handler;
+ }
+
+ if (cpu_boosting_enqueue_by_socket(client_sock) < 0) {
+ goto remove_handler;
+ }
+
+ return G_SOURCE_CONTINUE;
+
+remove_handler:
+ return G_SOURCE_REMOVE;
+}
+
+/* Client asks connect() to the resourced */
+bool cpu_boosting_connect_from_client(int fd, void *data)
+{
+ int ret;
+ int master_sock = fd;
+ static fd_handler_h handler;
+ socklen_t len;
+ struct sockaddr_un cli = { 0 };
+
+ if (master_sock <= 0) {
+ _E("[CPU-BOOSTING] master socket should be larger than 0");
+ goto remove_handler;
+ }
+
+ int new_sock = accept(master_sock, (struct sockaddr *)&cli, &len);
+ if (new_sock < 0) {
+ _E("[CPU-BOOSTING] Failed to allocate a new socket for the client");
+ goto continue_handler;
+ }
+
+ /* Add event handler to receive data from the client */
+ ret = add_fd_read_handler(new_sock, cpu_boosting_recv_from_client,
+ NULL, NULL, &handler);
+ if (ret < 0) {
+ _E("[CPU-BOOSTING] Failed to add event handler of (sock = %d)", new_sock);
+ close(new_sock);
+ }
+
+continue_handler:
+ return G_SOURCE_CONTINUE;
+
+remove_handler:
+ return G_SOURCE_REMOVE;
+}
+
+static int cpu_boosting_init_server(void)
+{
+ int ret;
+ static fd_handler_h handler;
+ int sock = cpu_boosting_init_socket();
+ if (sock > 0) {
+ ret = add_fd_read_handler(sock, cpu_boosting_connect_from_client,
+ NULL, NULL, &handler);
+ if (ret < 0) {
+ _E("[CPU-BOOSTING] Failed to add event handler of (sock = %d)", sock);
+ close(sock);
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+ else
+ return RESOURCED_ERROR_FAIL;
+
+ return RESOURCED_ERROR_NONE;
+}
+
/* Called by resourced main thread */
static int cpu_boosting_init(void *data)
{
cpu_boosting_thread_activate();
tid_table = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, NULL);
g_assert(tid_table);
+ /* For the socket-based client */
+ if (cpu_boosting_init_server() < 0)
+ _E("[CPU-BOOSTING] Failed to initialize server environment");
+ else
+ _D("[CPU-BOOSTING] Success to initialize server environment");
+ /* For the conf-based client */
register_notifier(RESOURCED_NOTIFIER_BOOSTING_RESOURCE, cpu_boosting_enqueue_by_conf);
return RESOURCED_ERROR_NONE;
}