1/* @title: DPCs */
2#pragma once
3#include <atomic.h>
4#include <stdbool.h>
5#include <stddef.h>
6#include <stdint.h>
7#include <structures/cpu_mask.h>
8
9struct dpc;
10struct dpc_context;
11
12typedef void (*dpc_func_t)(struct dpc_context *d, void *a, void *b);
13
14struct dpc {
15 dpc_func_t func;
16 void *a;
17 void *b;
18 atomic(struct dpc *) next; /* for MPSC push */
19 atomic_bool enqueued; /* prevents double-enqueue */
20};
21
22struct dpc_context {
23 struct dpc *dpc;
24};
25
26typedef void (*dpc_fanout_fn_t)(void);
27
28struct dpc_queue {
29 atomic(struct dpc *) head;
30};
31
32/* Per-cpu DPC data */
33struct dpc_cpu {
34 struct dpc_queue queue;
35};
36
37void dpc_drain_local(void);
38void dpc_run_local(void);
39void dpc_run_dpcs_from_irq(void);
40struct dpc *dpc_create(dpc_func_t fn, void *a, void *b);
41struct dpc *dpc_init(struct dpc *d, dpc_func_t fn, void *a, void *b);
42
43/* Run fn once on every CPU in cpus, returning only after all of them complete.
44 *
45 * NOTE: storage must hold at least popcount(cpus) DPCs, this is what
46 * the caller must uphold. storage entries get overwritten every call as well */
47void dpc_fanout(struct dpc *cc_nonnull_ptr storage, struct cpu_mask cpus,
48 dpc_fanout_fn_t cc_nonnull_ptr fn);
49void dpc_init_percpu(void);
50bool dpc_enqueue_local(struct dpc *d);
51bool dpc_enqueue_on_cpu(size_t cpu, struct dpc *cc_nonnull_ptr d);
52