1 // 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 <env.h>
9 #include <malloc.h>
10 #include <vsprintf.h>
11 #include <uthread.h>
12
13 /* Spawn arguments and job index */
14 struct 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 */
25 static unsigned int job[CONFIG_CMD_SPAWN_NUM_JOBS];
26 /* Return values of the commands run as jobs */
27 static enum command_ret_t job_ret[CONFIG_CMD_SPAWN_NUM_JOBS];
28
spa_free(struct spa * spa)29 static 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
spa_create(int argc,char * const argv[])42 static 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;
60 err:
61 spa_free(spa);
62 return NULL;
63 }
64
spawn_thread(void * arg)65 static 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
next_job_id(void)76 static 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
refresh_jobs(void)88 static 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
do_spawn(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])98 static 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
135 U_BOOT_CMD(spawn, CONFIG_SYS_MAXARGS, 0, do_spawn,
136 "run commands and summarize execution time",
137 "command [args...]\n");
138
wait_job(unsigned int idx)139 static 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
do_wait(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])158 static 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);
173 if (id < 1 || id > CONFIG_CMD_SPAWN_NUM_JOBS)
174 return CMD_RET_USAGE;
175 idx = (int)id - 1;
176 ret = wait_job(idx);
177 }
178 }
179
180 return ret;
181 }
182
183 U_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");
189