Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kernel: Introduce k_triggered_work #14041

Merged
merged 2 commits into from
Oct 4, 2019

Conversation

pizi-nordic
Copy link
Collaborator

@pizi-nordic pizi-nordic commented Mar 4, 2019

This PR introduces triggered work. The triggered work is submitted to workqueue when one of watched events happen. Such mechanism allows to eliminate costly dedicated threads to execute something when data is available or some resource become ready.

The work is driven mostly by device power management (details: #13382 (comment)) and @pdunaj needs.

Fixes: #10748

@zephyrbot
Copy link
Collaborator

zephyrbot commented Mar 4, 2019

All checks are passing now.

Review history of this comment for details about previous failed status.
Note that some checks might have not completed yet.

Copy link
Contributor

@andyross andyross left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be a little more general to have a callback-based API where a kernel function can be registered on a pollable object that gets called on event state changes, then implement k_triggered_work() in terms of that? That strikes me as useful too, and that way we won't have to implement it as two separate subsystems.

@pabigot
Copy link
Collaborator

pabigot commented Mar 4, 2019

@BFrog and @microbuilder might be able to provide feedback as to whether this feature would help solve sensor notification problems where #13718 (comment) suggested that callbacks are problematic but we still want to get rid of coupling sensor drivers to a specific thread hand-off solution.

I don't fully understand @andyross's suggestion but if its intent is to support immediate execution of specific actions ("kernel function") on event notifications, where one of the available actions is submitting work to a queue, I could see that being more general. Examples of other useful triggers would help motivate the generalization.

@pdunaj
Copy link
Collaborator

pdunaj commented Mar 5, 2019

@andyross

a callback-based API where a kernel function can be registered on a pollable object that gets called on event state changes

I am not sure this would work well. If state change happens from atomic context (e.g. interrupt) some API (e.g. mutex) would be unusable. Triggering work with pollable objects makes code executed from workq thread which has fewer limitations. Triggered work should work in userspace.

As an alternative I was also thinking about creating callback manager that would have its own thread and poll on behalf of others. This is however more expressive from the RAM perspective as you have to create yet another thread and yet another stack.

@pizi-nordic
Copy link
Collaborator Author

I have updated the PR introducing a callback into _poller structure. I think that this is the thing suggested by @andyross as it allows for easy extension in the future.

I have kept the callback internal to the polling code, as exporting it to the user might create a lot of trouble: It is called with interupts disabled from various contexts.

@pizi-nordic pizi-nordic force-pushed the k_triggered_work branch 4 times, most recently from 1403599 to f7b2c12 Compare March 6, 2019 15:57
@andyross
Copy link
Contributor

andyross commented Mar 6, 2019

To be clear: my thinking here is targetted more at how to evolve poll instead of bolting things onto it. IMHO poll (not really the subject of this patch per se) is implemented at the wrong level of abstraction. Threads that wait for something, in Zephyr, are always waiting on a wait_q. So the wait_q should be 1:1 with our "signalable event" thing. But that's not the way poll works, so everything needs to be special cased ("Oh, you're waiting on a semaphore, that's different than being pended on a fifo and I'm going to handle them with a big switch statement").

I'd like to get those switches and special cases out of poll, and make "signal interest in multiple wait_q's" a single lower level operation. In that world, having special cases on what to do after the signal (i.e. ready the thread vs. add a work item) has all the same problems and I'd like to have the low level operation be as decoupled from the API as possible.

@pizi-nordic
Copy link
Collaborator Author

@andyross: What you propose will not work with the k_triggered_work interface introduced here. I simply do not have a thread which I could put in the wait_q.

@pizi-nordic pizi-nordic changed the title [RFC] kernel: Introduce k_triggered_work kernel: Introduce k_triggered_work Mar 11, 2019
@pizi-nordic pizi-nordic force-pushed the k_triggered_work branch 2 times, most recently from ae27adb to ff8809c Compare March 11, 2019 15:20
kernel/poll.c Outdated

int _impl_k_poll(struct k_poll_event *events, int num_events, s32_t timeout)
{
__ASSERT(!_is_in_isr(), "");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asserts after the variable declarations

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@pizi-nordic
Copy link
Collaborator Author

pizi-nordic commented Sep 25, 2019

@pabigot: I added basic tests for the new functionality.

Copy link
Collaborator

@pabigot pabigot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems ok. There's enough that I'm not entirely happy about that I don't want to go ahead and approve it. There's nothing here that I would, at this moment, insist be changed, but I don't think there's been enough eyes on it from a functional perspective to just merge it.

I'd still like to see it named k_work_poll since it's essentially a threadless k_poll.

Also the timeout addition should be squashed into the commit that adds the new API.

When a work item timeout occurs I believe it would be better to delay disabling the poller until the wrapper work handler clears the event registry. That would require separate tracking of whether the timeout fired and whether events were discovered; e.g. add to struct k_work_triggered a signaled flag that gets set in k_work_triggered_poller_cb just as expired is set in triggered_work_expiration_handler. Then the consumer can optimally handle any events that occur between the timeout and the point where the work handler gets invoked rather than assuming there's nothing to do except timeout activity.

@andyross or somebody from the kernel team should determine whether the addition of CONFIG_POLL=y to tests/kernel/workq/work_queue is appropriate, since that eliminates the former use of that test when CONFIG_POLL is not selected.

@@ -1,3 +1,4 @@
CONFIG_ZTEST=y
CONFIG_QEMU_TICKLESS_WORKAROUND=y
CONFIG_POLL=y
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this addition please remove the section of testcase.yaml that runs this test with CONFIG_POLL=y.

Or it may be that the new API needs to be in a separate test so the behavior of workq without CONFIG_POLL continues to be tested. For that we need input from a kernel maintainer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

kernel/poll.c Outdated
k_work_submit_to_queue(work_q, &twork->work);
}

static int k_work_triggered_poller_cb(struct k_poll_event *event, u32_t status)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with other internal functions this should be triggered_work_poller_cb. The k_ prefix makes it look like public API.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

kernel/poll.c Outdated
struct _poller *poller = event->poller;

if (poller->is_polling && poller->thread) {
struct k_work_triggered *work =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In at least one other function (the expiration handler above) this variable is twork to distinguish it from the contained struct k_work member. Consistent naming that indicates the type of the pointed-to value would be nice.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@pizi-nordic pizi-nordic force-pushed the k_triggered_work branch 3 times, most recently from 82d3518 to b1cadc1 Compare September 27, 2019 11:09
@pizi-nordic
Copy link
Collaborator Author

When a work item timeout occurs I believe it would be better to delay disabling the poller until the wrapper work handler clears the event registry. That would require separate tracking of whether the timeout fired and whether events were discovered; e.g. add to struct k_work_triggered a signaled flag that gets set in k_work_triggered_poller_cb just as expired is set in triggered_work_expiration_handler. Then the consumer can optimally handle any events that occur between the timeout and the point where the work handler gets invoked rather than assuming there's nothing to do except timeout activity.

I think that it is possible now:

  • If expired flag is false, the event happened before timeout.
  • If expired flag true, the timeout happened before any event. However the events are still monitored after timeout up to clean-up phase. As result the application could scan the events to check if something changed state after timeout and before execution.

@pabigot
Copy link
Collaborator

pabigot commented Sep 27, 2019

As result the application could scan the events to check if something changed state after timeout and before execution.

Yes, but it's more accurate to say that the application must scan the events, as once the timeout occurs nothing outside the event array records the fact of a subsequent signal. In k_poll there's a binary choice between 0 and -EAGAIN, which is fine there because there is no significant interval between the timeout and clearing the event registration. For the work item there's an arbitrary delay between those two steps which increases the likelihood of an event being signalled but not announced.

I had proposed:

for non-error situations [k_work_poll] will store the return code that k_poll would have returned (indicating either timeout or event) into a field in struct k_work_poll, so the work item can handle both timeout and event triggers.

Switching from bool expired to int poll_result would seem to make sense as it's compatible with existing API. Unfortunately that API doesn't support representing that both timeout and an event signal have occurred, though we could do a post-timeout change of the result from -EAGAIN to 0 when an event comes in, which would be pretty natural. We could also handle -EINTR too.

Again I'm not requesting that you do this; I still want other eyes on it before approving. As is it's a nice feature (though with surprisingly little interest); it could just have better compatibility with existing API, IMO.

@pabigot
Copy link
Collaborator

pabigot commented Oct 1, 2019

requested by @pizi-nordic for API meeting: provide input on #14041 (comment) i.e. whether API should follow k_poll in name and return value, or remain distinct as k_work_triggered.

Prototype in last commits of https://github.com/pabigot/zephyr/commits/pr/14041

attn @carlescufi

@pabigot pabigot added the dev-review To be discussed in dev-review meeting label Oct 1, 2019
@pabigot
Copy link
Collaborator

pabigot commented Oct 1, 2019

Added to dev-review since #14041 (comment) didn't get time in API telecon. @pizi-nordic would really like to get the API settled so this can be merged.

@galak
Copy link
Collaborator

galak commented Oct 3, 2019

dev-review: meeting consensus was to go with @pabigot proposal and rename to k_work_poll as the prefix

@galak galak removed the dev-review To be discussed in dev-review meeting label Oct 3, 2019
This commit separates k_poll() infrastructure from k_poll() API
implementation, allowing other (future) API calls to use the same
framework.

Signed-off-by: Piotr Zięcik <piotr.ziecik@nordicsemi.no>
@pizi-nordic pizi-nordic force-pushed the k_triggered_work branch 3 times, most recently from 76ed74c to fb81402 Compare October 4, 2019 10:12
Copy link
Collaborator

@pabigot pabigot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two small documentation changes (fix link, mention submit on timeout).

The :cpp:func:`k_work_poll()` interface schedules a triggered work
item in response to a **poll event** (see :ref:`polling_v2`), that will
call a user-defined function when the monitored resource becomes available
or a poll signal is raised. In contrast to :cpp:func:`k_poll()`,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

" the a monitored resource becomes available or a poll signal is raised, or a timeout occurs."

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Triggered Work
**************

The :cpp:func:`k_work_poll()` interface schedules a triggered work
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be k_work_poll_submit() for the documentation link to work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

kernel/poll.c Outdated
.thread = _current,
.cb = k_poll_poller_cb };

__ASSERT(!z_is_in_isr(), "");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, didn't run it first: this needs to be z_arch_is_in_isr() (upstream rename).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

This commit adds new k_work_poll interface. It allows to
submit given work to a workqueue automatically when one of the
watched pollable objects changes its state.

Signed-off-by: Piotr Zięcik <piotr.ziecik@nordicsemi.no>
Signed-off-by: Peter A. Bigot <pab@pabigot.com>
@carlescufi carlescufi merged commit 19d8349 into zephyrproject-rtos:master Oct 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: API Changes to public APIs area: Documentation area: Kernel area: Tests Issues related to a particular existing or missing test
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Work waiting on pollable objects
10 participants