| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (c) 2011 The Chromium OS Authors. |
| */ |
| |
| #include <command.h> |
| #include <console.h> |
| #include <malloc.h> |
| #include <vsprintf.h> |
| #include <uthread.h> |
| |
| /* Spawn arguments and job index */ |
| struct spa { |
| int argc; |
| char **argv; |
| unsigned int job_idx; |
| }; |
| |
| /* |
| * uthread group identifiers for each running job |
| * 0: job slot available, != 0: uthread group id |
| * Note that job[0] is job_id 1, job[1] is job_id 2 etc. |
| */ |
| static unsigned int job[CONFIG_CMD_SPAWN_NUM_JOBS]; |
| /* Return values of the commands run as jobs */ |
| static enum command_ret_t job_ret[CONFIG_CMD_SPAWN_NUM_JOBS]; |
| |
| static void spa_free(struct spa *spa) |
| { |
| int i; |
| |
| if (!spa) |
| return; |
| |
| for (i = 0; i < spa->argc; i++) |
| free(spa->argv[i]); |
| free(spa->argv); |
| free(spa); |
| } |
| |
| static struct spa *spa_create(int argc, char *const argv[]) |
| { |
| struct spa *spa; |
| int i; |
| |
| spa = calloc(1, sizeof(*spa)); |
| if (!spa) |
| return NULL; |
| spa->argc = argc; |
| spa->argv = malloc(argc * sizeof(char *)); |
| if (!spa->argv) |
| goto err; |
| for (i = 0; i < argc; i++) { |
| spa->argv[i] = strdup(argv[i]); |
| if (!spa->argv[i]) |
| goto err; |
| } |
| return spa; |
| err: |
| spa_free(spa); |
| return NULL; |
| } |
| |
| static void spawn_thread(void *arg) |
| { |
| struct spa *spa = (struct spa *)arg; |
| ulong cycles = 0; |
| int repeatable = 0; |
| |
| job_ret[spa->job_idx] = cmd_process(0, spa->argc, spa->argv, |
| &repeatable, &cycles); |
| spa_free(spa); |
| } |
| |
| static unsigned int next_job_id(void) |
| { |
| int i; |
| |
| for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++) |
| if (!job[i]) |
| return i + 1; |
| |
| /* No job available */ |
| return 0; |
| } |
| |
| static void refresh_jobs(void) |
| { |
| int i; |
| |
| for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++) |
| if (job[i] && uthread_grp_done(job[i])) |
| job[i] = 0; |
| |
| } |
| |
| static int do_spawn(struct cmd_tbl *cmdtp, int flag, int argc, |
| char *const argv[]) |
| { |
| unsigned int id; |
| unsigned int idx; |
| struct spa *spa; |
| int ret; |
| |
| if (argc == 1) |
| return CMD_RET_USAGE; |
| |
| spa = spa_create(argc - 1, argv + 1); |
| if (!spa) |
| return CMD_RET_FAILURE; |
| |
| refresh_jobs(); |
| |
| id = next_job_id(); |
| if (!id) |
| return CMD_RET_FAILURE; |
| idx = id - 1; |
| |
| job[idx] = uthread_grp_new_id(); |
| |
| ret = uthread_create(NULL, spawn_thread, spa, 0, job[idx]); |
| if (ret) { |
| job[idx] = 0; |
| return CMD_RET_FAILURE; |
| } |
| |
| ret = env_set_ulong("job_id", id); |
| if (ret) |
| return CMD_RET_FAILURE; |
| |
| return CMD_RET_SUCCESS; |
| } |
| |
| U_BOOT_CMD(spawn, CONFIG_SYS_MAXARGS, 0, do_spawn, |
| "run commands and summarize execution time", |
| "command [args...]\n"); |
| |
| static enum command_ret_t wait_job(unsigned int idx) |
| { |
| int prev = disable_ctrlc(false); |
| |
| while (!uthread_grp_done(job[idx])) { |
| if (ctrlc()) { |
| puts("<INTERRUPT>\n"); |
| disable_ctrlc(prev); |
| return CMD_RET_FAILURE; |
| } |
| uthread_schedule(); |
| } |
| |
| job[idx] = 0; |
| disable_ctrlc(prev); |
| |
| return job_ret[idx]; |
| } |
| |
| static int do_wait(struct cmd_tbl *cmdtp, int flag, int argc, |
| char *const argv[]) |
| { |
| enum command_ret_t ret = CMD_RET_SUCCESS; |
| unsigned long id; |
| unsigned int idx; |
| int i; |
| |
| if (argc == 1) { |
| for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++) |
| if (job[i]) |
| ret = wait_job(i); |
| } else { |
| for (i = 1; i < argc; i++) { |
| id = dectoul(argv[i], NULL); |
| if (id < 0 || id > CONFIG_CMD_SPAWN_NUM_JOBS) |
| return CMD_RET_USAGE; |
| idx = (int)id - 1; |
| ret = wait_job(idx); |
| } |
| } |
| |
| return ret; |
| } |
| |
| U_BOOT_CMD(wait, CONFIG_SYS_MAXARGS, 0, do_wait, |
| "wait for one or more jobs to complete", |
| "[job_id ...]\n" |
| " - Wait until all specified jobs have exited and return the\n" |
| " exit status of the last job waited for. When no job_id is\n" |
| " given, wait for all the background jobs.\n"); |