blob: 093581235206101615c492c74526fc395d54cbb4 [file] [log] [blame]
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_SYS_P4WQ_H_
#define ZEPHYR_INCLUDE_SYS_P4WQ_H_
#include <kernel.h>
/* Zephyr Pooled Parallel Preemptible Priority-based Work Queues */
struct k_p4wq_work;
/**
* P4 Queue handler callback
*/
typedef void (*k_p4wq_handler_t)(struct k_p4wq_work *work);
/**
* @brief P4 Queue Work Item
*
* User-populated struct representing a single work item. The
* priority and deadline fields are interpreted as thread scheduling
* priorities, exactly as per k_thread_priority_set() and
* k_thread_deadline_set().
*/
struct k_p4wq_work {
/* Filled out by submitting code */
int32_t priority;
int32_t deadline;
k_p4wq_handler_t handler;
bool sync;
struct k_sem done_sem;
/* reserved for implementation */
union {
struct rbnode rbnode;
sys_dlist_t dlnode;
};
struct k_thread *thread;
struct k_p4wq *queue;
};
#define K_P4WQ_QUEUE_PER_THREAD BIT(0)
#define K_P4WQ_DELAYED_START BIT(1)
#define K_P4WQ_USER_CPU_MASK BIT(2)
/**
* @brief P4 Queue
*
* Kernel pooled parallel preemptible priority-based work queue
*/
struct k_p4wq {
struct k_spinlock lock;
/* Pending threads waiting for work items
*
* FIXME: a waitq isn't really the right data structure here.
* Wait queues are priority-sorted, but we don't want that
* sorting overhead since we're effectively doing it ourselves
* by directly mutating the priority when a thread is
* unpended. We just want "blocked threads on a list", but
* there's no clean scheduler API for that.
*/
_wait_q_t waitq;
/* Work items waiting for processing */
struct rbtree queue;
/* Work items in progress */
sys_dlist_t active;
/* K_P4WQ_* flags above */
uint32_t flags;
};
struct k_p4wq_initparam {
uint32_t num;
uintptr_t stack_size;
struct k_p4wq *queue;
struct k_thread *threads;
struct z_thread_stack_element *stacks;
uint32_t flags;
};
/**
* @brief Statically initialize a P4 Work Queue
*
* Statically defines a struct k_p4wq object with the specified number
* of threads which will be initialized at boot and ready for use on
* entry to main().
*
* @param name Symbol name of the struct k_p4wq that will be defined
* @param n_threads Number of threads in the work queue pool
* @param stack_sz Requested stack size of each thread, in bytes
*/
#define K_P4WQ_DEFINE(name, n_threads, stack_sz) \
static K_THREAD_STACK_ARRAY_DEFINE(_p4stacks_##name, \
n_threads, stack_sz); \
static struct k_thread _p4threads_##name[n_threads]; \
static struct k_p4wq name; \
static const Z_STRUCT_SECTION_ITERABLE(k_p4wq_initparam, \
_init_##name) = { \
.num = n_threads, \
.stack_size = stack_sz, \
.threads = _p4threads_##name, \
.stacks = &(_p4stacks_##name[0][0]), \
.queue = &name, \
.flags = 0, \
}
/**
* @brief Statically initialize an array of P4 Work Queues
*
* Statically defines an array of struct k_p4wq objects with the specified
* number of threads which will be initialized at boot and ready for use on
* entry to main().
*
* @param name Symbol name of the struct k_p4wq array that will be defined
* @param n_threads Number of threads and work queues
* @param stack_sz Requested stack size of each thread, in bytes
* @param flg Flags
*/
#define K_P4WQ_ARRAY_DEFINE(name, n_threads, stack_sz, flg) \
static K_THREAD_STACK_ARRAY_DEFINE(_p4stacks_##name, \
n_threads, stack_sz); \
static struct k_thread _p4threads_##name[n_threads]; \
static struct k_p4wq name[n_threads]; \
static const Z_STRUCT_SECTION_ITERABLE(k_p4wq_initparam, \
_init_##name) = { \
.num = n_threads, \
.stack_size = stack_sz, \
.threads = _p4threads_##name, \
.stacks = &(_p4stacks_##name[0][0]), \
.queue = name, \
.flags = K_P4WQ_QUEUE_PER_THREAD | flg, \
}
/**
* @brief Initialize P4 Queue
*
* Initializes a P4 Queue object. These objects must be initialized
* via this function (or statically using K_P4WQ_DEFINE) before any
* other API calls are made on it.
*
* @param queue P4 Queue to initialize
*/
void k_p4wq_init(struct k_p4wq *queue);
/**
* @brief Dynamically add a thread object to a P4 Queue pool
*
* Adds a thread to the pool managed by a P4 queue. The thread object
* must not be in use. If k_thread_create() has previously been
* called on it, it must be aborted before being given to the queue.
*
* @param queue P4 Queue to which to add the thread
* @param thread Uninitialized/aborted thread object to add
* @param stack Thread stack memory
* @param stack_size Thread stack size
*/
void k_p4wq_add_thread(struct k_p4wq *queue, struct k_thread *thread,
k_thread_stack_t *stack,
size_t stack_size);
/**
* @brief Submit work item to a P4 queue
*
* Submits the specified work item to the queue. The caller must have
* already initialized the relevant fields of the struct. The queue
* will execute the handler when CPU time is available and when no
* higher-priority work items are available. The handler may be
* invoked on any CPU.
*
* The caller must not mutate the struct while it is stored in the
* queue. The memory should remain unchanged until k_p4wq_cancel() is
* called or until the entry to the handler function.
*
* @note This call is a scheduling point, so if the submitted item (or
* any other ready thread) has a higher priority than the current
* thread and the current thread has a preemptible priority then the
* caller will yield.
*
* @param queue P4 Queue to which to submit
* @param item P4 work item to be submitted
*/
void k_p4wq_submit(struct k_p4wq *queue, struct k_p4wq_work *item);
/**
* @brief Cancel submitted P4 work item
*
* Cancels a previously-submitted work item and removes it from the
* queue. Returns true if the item was found in the queue and
* removed. If the function returns false, either the item was never
* submitted, has already been executed, or is still running.
*
* @return true if the item was successfully removed, otherwise false
*/
bool k_p4wq_cancel(struct k_p4wq *queue, struct k_p4wq_work *item);
/**
* @brief Regain ownership of the work item, wait for completion if it's synchronous
*/
int k_p4wq_wait(struct k_p4wq_work *work, k_timeout_t timeout);
void k_p4wq_enable_static_thread(struct k_p4wq *queue, struct k_thread *thread,
uint32_t cpu_mask);
#endif /* ZEPHYR_INCLUDE_SYS_P4WQ_H_ */