// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2011 The Chromium OS Authors. */ #include #include #include #include #include #include /* 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("\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 < 1 || 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");