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