blob: eddbcb792b32c76fa171adfcc469c2fbbe50d043 [file] [log] [blame]
Jerome Forissier670df4e2025-04-18 16:09:42 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2011 The Chromium OS Authors.
4 */
5
6#include <command.h>
7#include <console.h>
8#include <malloc.h>
9#include <vsprintf.h>
10#include <uthread.h>
11
12/* Spawn arguments and job index */
13struct spa {
14 int argc;
15 char **argv;
16 unsigned int job_idx;
17};
18
19/*
20 * uthread group identifiers for each running job
21 * 0: job slot available, != 0: uthread group id
22 * Note that job[0] is job_id 1, job[1] is job_id 2 etc.
23 */
24static unsigned int job[CONFIG_CMD_SPAWN_NUM_JOBS];
25/* Return values of the commands run as jobs */
26static enum command_ret_t job_ret[CONFIG_CMD_SPAWN_NUM_JOBS];
27
28static void spa_free(struct spa *spa)
29{
30 int i;
31
32 if (!spa)
33 return;
34
35 for (i = 0; i < spa->argc; i++)
36 free(spa->argv[i]);
37 free(spa->argv);
38 free(spa);
39}
40
41static struct spa *spa_create(int argc, char *const argv[])
42{
43 struct spa *spa;
44 int i;
45
46 spa = calloc(1, sizeof(*spa));
47 if (!spa)
48 return NULL;
49 spa->argc = argc;
50 spa->argv = malloc(argc * sizeof(char *));
51 if (!spa->argv)
52 goto err;
53 for (i = 0; i < argc; i++) {
54 spa->argv[i] = strdup(argv[i]);
55 if (!spa->argv[i])
56 goto err;
57 }
58 return spa;
59err:
60 spa_free(spa);
61 return NULL;
62}
63
64static void spawn_thread(void *arg)
65{
66 struct spa *spa = (struct spa *)arg;
67 ulong cycles = 0;
68 int repeatable = 0;
69
70 job_ret[spa->job_idx] = cmd_process(0, spa->argc, spa->argv,
71 &repeatable, &cycles);
72 spa_free(spa);
73}
74
75static unsigned int next_job_id(void)
76{
77 int i;
78
79 for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++)
80 if (!job[i])
81 return i + 1;
82
83 /* No job available */
84 return 0;
85}
86
87static void refresh_jobs(void)
88{
89 int i;
90
91 for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++)
92 if (job[i] && uthread_grp_done(job[i]))
93 job[i] = 0;
94
95}
96
97static int do_spawn(struct cmd_tbl *cmdtp, int flag, int argc,
98 char *const argv[])
99{
100 unsigned int id;
101 unsigned int idx;
102 struct spa *spa;
103 int ret;
104
105 if (argc == 1)
106 return CMD_RET_USAGE;
107
108 spa = spa_create(argc - 1, argv + 1);
109 if (!spa)
110 return CMD_RET_FAILURE;
111
112 refresh_jobs();
113
114 id = next_job_id();
115 if (!id)
116 return CMD_RET_FAILURE;
117 idx = id - 1;
118
119 job[idx] = uthread_grp_new_id();
120
121 ret = uthread_create(NULL, spawn_thread, spa, 0, job[idx]);
122 if (ret) {
123 job[idx] = 0;
124 return CMD_RET_FAILURE;
125 }
126
127 ret = env_set_ulong("job_id", id);
128 if (ret)
129 return CMD_RET_FAILURE;
130
131 return CMD_RET_SUCCESS;
132}
133
134U_BOOT_CMD(spawn, CONFIG_SYS_MAXARGS, 0, do_spawn,
135 "run commands and summarize execution time",
136 "command [args...]\n");
137
138static enum command_ret_t wait_job(unsigned int idx)
139{
140 int prev = disable_ctrlc(false);
141
142 while (!uthread_grp_done(job[idx])) {
143 if (ctrlc()) {
144 puts("<INTERRUPT>\n");
145 disable_ctrlc(prev);
146 return CMD_RET_FAILURE;
147 }
148 uthread_schedule();
149 }
150
151 job[idx] = 0;
152 disable_ctrlc(prev);
153
154 return job_ret[idx];
155}
156
157static int do_wait(struct cmd_tbl *cmdtp, int flag, int argc,
158 char *const argv[])
159{
160 enum command_ret_t ret = CMD_RET_SUCCESS;
161 unsigned long id;
162 unsigned int idx;
163 int i;
164
165 if (argc == 1) {
166 for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++)
167 if (job[i])
168 ret = wait_job(i);
169 } else {
170 for (i = 1; i < argc; i++) {
171 id = dectoul(argv[i], NULL);
172 if (id < 0 || id > CONFIG_CMD_SPAWN_NUM_JOBS)
173 return CMD_RET_USAGE;
174 idx = (int)id - 1;
175 ret = wait_job(idx);
176 }
177 }
178
179 return ret;
180}
181
182U_BOOT_CMD(wait, CONFIG_SYS_MAXARGS, 0, do_wait,
183 "wait for one or more jobs to complete",
184 "[job_id ...]\n"
185 " - Wait until all specified jobs have exited and return the\n"
186 " exit status of the last job waited for. When no job_id is\n"
187 " given, wait for all the background jobs.\n");