summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2020-05-14 20:21:52 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2020-08-13 13:55:22 +0200
commit04e54a0cf635de24656e7818cddb25333d284c79 (patch)
tree2be5c9248c4955c2637ef0a49313517a1a9a05f1 /common
parent94b264aca5685aeba5fb6babc097d64b78478a11 (diff)
downloadbarebox-04e54a0cf635de24656e7818cddb25333d284c79.tar.gz
barebox-04e54a0cf635de24656e7818cddb25333d284c79.tar.xz
Add workqueues
Some code wants to run arbitrary code in the background, examples are fastboot and ratp. Currently this is implemented in pollers. The problem with this is that pollers are executed whenever is_timeout() is called which may happen anywhere in the code. With this we can never tell which resources are currently in use when the poller is executed. This adds a work queue interface which is specifically designed to trigger doing work in a context where it's safe to run arbitrary commands. Code in pollers can attach work to a work queue which is at that time only queued up. A new slice, the command slice, is added which by default is acquired. It is only released at places where the shell waits for input. When during this time pollers are executed the queued up works are done before running the registered pollers. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'common')
-rw-r--r--common/Makefile1
-rw-r--r--common/hush.c6
-rw-r--r--common/poller.c9
-rw-r--r--common/slice.c32
-rw-r--r--common/startup.c3
-rw-r--r--common/workqueue.c58
6 files changed, 109 insertions, 0 deletions
diff --git a/common/Makefile b/common/Makefile
index 58594d9782..99f1977b4b 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_ELF) += elf.o
obj-y += restart.o
obj-y += poweroff.o
obj-y += slice.o
+obj-y += workqueue.o
obj-$(CONFIG_MACHINE_ID) += machine_id.o
obj-$(CONFIG_AUTO_COMPLETE) += complete.o
obj-y += version.o
diff --git a/common/hush.c b/common/hush.c
index c24b2c7cd2..ec0d5129b7 100644
--- a/common/hush.c
+++ b/common/hush.c
@@ -121,6 +121,7 @@
#include <libbb.h>
#include <password.h>
#include <glob.h>
+#include <slice.h>
#include <getopt.h>
#include <libfile.h>
#include <libbb.h>
@@ -460,7 +461,12 @@ static void get_user_input(struct in_str *i)
else
prompt = CONFIG_PROMPT_HUSH_PS2;
+ command_slice_release();
+
n = readline(prompt, console_buffer, CONFIG_CBSIZE);
+
+ command_slice_acquire();
+
if (n == -1 ) {
i->interrupt = 1;
n = 0;
diff --git a/common/poller.c b/common/poller.c
index 95f828b439..7b1b92714c 100644
--- a/common/poller.c
+++ b/common/poller.c
@@ -12,6 +12,8 @@
#include <param.h>
#include <poller.h>
#include <clock.h>
+#include <work.h>
+#include <slice.h>
static LIST_HEAD(poller_list);
static int poller_active;
@@ -110,15 +112,22 @@ int poller_async_unregister(struct poller_async *pa)
void poller_call(void)
{
struct poller_struct *poller, *tmp;
+ bool run_workqueues = !slice_acquired(&command_slice);
if (poller_active)
return;
+ command_slice_acquire();
+
+ if (run_workqueues)
+ wq_do_all_works();
+
poller_active = 1;
list_for_each_entry_safe(poller, tmp, &poller_list, list)
poller->func(poller);
+ command_slice_release();
poller_active = 0;
}
diff --git a/common/slice.c b/common/slice.c
index 085d67604f..9d7e0d16cf 100644
--- a/common/slice.c
+++ b/common/slice.c
@@ -3,6 +3,7 @@
#define pr_fmt(fmt) "slice: " fmt
#include <common.h>
+#include <init.h>
#include <slice.h>
/*
@@ -231,6 +232,37 @@ void slice_exit(struct slice *slice)
free(slice->name);
}
+struct slice command_slice;
+
+/**
+ * command_slice_acquire - acquire the command slice
+ *
+ * The command slice is acquired by default. It is only released
+ * at certain points we know it's safe to execute code in the
+ * background, like when the shell is waiting for input.
+ */
+void command_slice_acquire(void)
+{
+ slice_acquire(&command_slice);
+}
+
+/**
+ * command_slice_release - release the command slice
+ */
+void command_slice_release(void)
+{
+ slice_release(&command_slice);
+}
+
+static int command_slice_init(void)
+{
+ slice_init(&command_slice, "command");
+ slice_acquire(&command_slice);
+ return 0;
+}
+
+pure_initcall(command_slice_init);
+
#if defined CONFIG_CMD_SLICE
#include <command.h>
diff --git a/common/startup.c b/common/startup.c
index 1c58e41288..075863d22e 100644
--- a/common/startup.c
+++ b/common/startup.c
@@ -34,6 +34,7 @@
#include <debug_ll.h>
#include <fs.h>
#include <errno.h>
+#include <slice.h>
#include <linux/stat.h>
#include <envfs.h>
#include <magicvar.h>
@@ -272,8 +273,10 @@ enum autoboot_state do_autoboot_countdown(void)
break;
}
+ command_slice_release();
ret = console_countdown(global_autoboot_timeout, flags, abortkeys,
&outkey);
+ command_slice_acquire();
if (ret == 0)
autoboot_state = AUTOBOOT_BOOT;
diff --git a/common/workqueue.c b/common/workqueue.c
new file mode 100644
index 0000000000..6fdd4e42ea
--- /dev/null
+++ b/common/workqueue.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <common.h>
+#include <work.h>
+
+static void wq_do_pending_work(struct work_queue *wq)
+{
+ struct work_struct *work, *tmp;
+
+ list_for_each_entry_safe(work, tmp, &wq->work, list) {
+ list_del(&work->list);
+ wq->fn(work);
+ }
+}
+
+void wq_cancel_work(struct work_queue *wq)
+{
+ struct work_struct *work, *tmp;
+
+ list_for_each_entry_safe(work, tmp, &wq->work, list) {
+ list_del(&work->list);
+ wq->cancel(work);
+ }
+}
+
+static LIST_HEAD(work_queues);
+
+/**
+ * wq_do_all_works - do all pending work
+ *
+ * This calls all pending work functions
+ */
+void wq_do_all_works(void)
+{
+ struct work_queue *wq;
+
+ list_for_each_entry(wq, &work_queues, list)
+ wq_do_pending_work(wq);
+}
+
+/**
+ * wq_register - register a new work queue
+ * @wq: The work queue
+ */
+void wq_register(struct work_queue *wq)
+{
+ INIT_LIST_HEAD(&wq->work);
+ list_add_tail(&wq->list, &work_queues);
+}
+
+/**
+ * wq_unregister - unregister a work queue
+ * @wq: The work queue
+ */
+void wq_unregister(struct work_queue *wq)
+{
+ wq_cancel_work(wq);
+ list_del(&wq->list);
+}