mirror of
https://github.com/torvalds/linux.git
synced 2025-11-30 23:16:01 +07:00
wifi: cfg80211: add an hrtimer based delayed work item
The normal timer mechanism assume that timeout further in the future need a lower accuracy. As an example, the granularity for a timer scheduled 4096 ms in the future on a 1000 Hz system is already 512 ms. This granularity is perfectly sufficient for e.g. timeouts, but there are other types of events that will happen at a future point in time and require a higher accuracy. Add a new wiphy_hrtimer_work type that uses an hrtimer internally. The API is almost identical to the existing wiphy_delayed_work and it can be used as a drop-in replacement after minor adjustments. The work will be scheduled relative to the current time with a slack of 1 millisecond. CC: stable@vger.kernel.org # 6.4+ Signed-off-by: Benjamin Berg <benjamin.berg@intel.com> Reviewed-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com> Link: https://patch.msgid.link/20251028125710.7f13a2adc5eb.I01b5af0363869864b0580d9c2a1770bafab69566@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
committed by
Johannes Berg
parent
3b8694e56f
commit
7ceba45a66
@@ -6435,6 +6435,11 @@ static inline void wiphy_delayed_work_init(struct wiphy_delayed_work *dwork,
|
||||
* after wiphy_lock() was called. Therefore, wiphy_cancel_work() can
|
||||
* use just cancel_work() instead of cancel_work_sync(), it requires
|
||||
* being in a section protected by wiphy_lock().
|
||||
*
|
||||
* Note that these are scheduled with a timer where the accuracy
|
||||
* becomes less the longer in the future the scheduled timer is. Use
|
||||
* wiphy_hrtimer_work_queue() if the timer must be not be late by more
|
||||
* than approximately 10 percent.
|
||||
*/
|
||||
void wiphy_delayed_work_queue(struct wiphy *wiphy,
|
||||
struct wiphy_delayed_work *dwork,
|
||||
@@ -6506,6 +6511,79 @@ void wiphy_delayed_work_flush(struct wiphy *wiphy,
|
||||
bool wiphy_delayed_work_pending(struct wiphy *wiphy,
|
||||
struct wiphy_delayed_work *dwork);
|
||||
|
||||
struct wiphy_hrtimer_work {
|
||||
struct wiphy_work work;
|
||||
struct wiphy *wiphy;
|
||||
struct hrtimer timer;
|
||||
};
|
||||
|
||||
enum hrtimer_restart wiphy_hrtimer_work_timer(struct hrtimer *t);
|
||||
|
||||
static inline void wiphy_hrtimer_work_init(struct wiphy_hrtimer_work *hrwork,
|
||||
wiphy_work_func_t func)
|
||||
{
|
||||
hrtimer_setup(&hrwork->timer, wiphy_hrtimer_work_timer,
|
||||
CLOCK_BOOTTIME, HRTIMER_MODE_REL);
|
||||
wiphy_work_init(&hrwork->work, func);
|
||||
}
|
||||
|
||||
/**
|
||||
* wiphy_hrtimer_work_queue - queue hrtimer work for the wiphy
|
||||
* @wiphy: the wiphy to queue for
|
||||
* @hrwork: the high resolution timer worker
|
||||
* @delay: the delay given as a ktime_t
|
||||
*
|
||||
* Please refer to wiphy_delayed_work_queue(). The difference is that
|
||||
* the hrtimer work uses a high resolution timer for scheduling. This
|
||||
* may be needed if timeouts might be scheduled further in the future
|
||||
* and the accuracy of the normal timer is not sufficient.
|
||||
*
|
||||
* Expect a delay of a few milliseconds as the timer is scheduled
|
||||
* with some slack and some more time may pass between queueing the
|
||||
* work and its start.
|
||||
*/
|
||||
void wiphy_hrtimer_work_queue(struct wiphy *wiphy,
|
||||
struct wiphy_hrtimer_work *hrwork,
|
||||
ktime_t delay);
|
||||
|
||||
/**
|
||||
* wiphy_hrtimer_work_cancel - cancel previously queued hrtimer work
|
||||
* @wiphy: the wiphy, for debug purposes
|
||||
* @hrtimer: the hrtimer work to cancel
|
||||
*
|
||||
* Cancel the work *without* waiting for it, this assumes being
|
||||
* called under the wiphy mutex acquired by wiphy_lock().
|
||||
*/
|
||||
void wiphy_hrtimer_work_cancel(struct wiphy *wiphy,
|
||||
struct wiphy_hrtimer_work *hrtimer);
|
||||
|
||||
/**
|
||||
* wiphy_hrtimer_work_flush - flush previously queued hrtimer work
|
||||
* @wiphy: the wiphy, for debug purposes
|
||||
* @hrwork: the hrtimer work to flush
|
||||
*
|
||||
* Flush the work (i.e. run it if pending). This must be called
|
||||
* under the wiphy mutex acquired by wiphy_lock().
|
||||
*/
|
||||
void wiphy_hrtimer_work_flush(struct wiphy *wiphy,
|
||||
struct wiphy_hrtimer_work *hrwork);
|
||||
|
||||
/**
|
||||
* wiphy_hrtimer_work_pending - Find out whether a wiphy hrtimer
|
||||
* work item is currently pending.
|
||||
*
|
||||
* @wiphy: the wiphy, for debug purposes
|
||||
* @hrwork: the hrtimer work in question
|
||||
*
|
||||
* Return: true if timer is pending, false otherwise
|
||||
*
|
||||
* Please refer to the wiphy_delayed_work_pending() documentation as
|
||||
* this is the equivalent function for hrtimer based delayed work
|
||||
* items.
|
||||
*/
|
||||
bool wiphy_hrtimer_work_pending(struct wiphy *wiphy,
|
||||
struct wiphy_hrtimer_work *hrwork);
|
||||
|
||||
/**
|
||||
* enum ieee80211_ap_reg_power - regulatory power for an Access Point
|
||||
*
|
||||
|
||||
@@ -1787,6 +1787,62 @@ bool wiphy_delayed_work_pending(struct wiphy *wiphy,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wiphy_delayed_work_pending);
|
||||
|
||||
enum hrtimer_restart wiphy_hrtimer_work_timer(struct hrtimer *t)
|
||||
{
|
||||
struct wiphy_hrtimer_work *hrwork =
|
||||
container_of(t, struct wiphy_hrtimer_work, timer);
|
||||
|
||||
wiphy_work_queue(hrwork->wiphy, &hrwork->work);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wiphy_hrtimer_work_timer);
|
||||
|
||||
void wiphy_hrtimer_work_queue(struct wiphy *wiphy,
|
||||
struct wiphy_hrtimer_work *hrwork,
|
||||
ktime_t delay)
|
||||
{
|
||||
trace_wiphy_hrtimer_work_queue(wiphy, &hrwork->work, delay);
|
||||
|
||||
if (!delay) {
|
||||
hrtimer_cancel(&hrwork->timer);
|
||||
wiphy_work_queue(wiphy, &hrwork->work);
|
||||
return;
|
||||
}
|
||||
|
||||
hrwork->wiphy = wiphy;
|
||||
hrtimer_start_range_ns(&hrwork->timer, delay,
|
||||
1000 * NSEC_PER_USEC, HRTIMER_MODE_REL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wiphy_hrtimer_work_queue);
|
||||
|
||||
void wiphy_hrtimer_work_cancel(struct wiphy *wiphy,
|
||||
struct wiphy_hrtimer_work *hrwork)
|
||||
{
|
||||
lockdep_assert_held(&wiphy->mtx);
|
||||
|
||||
hrtimer_cancel(&hrwork->timer);
|
||||
wiphy_work_cancel(wiphy, &hrwork->work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wiphy_hrtimer_work_cancel);
|
||||
|
||||
void wiphy_hrtimer_work_flush(struct wiphy *wiphy,
|
||||
struct wiphy_hrtimer_work *hrwork)
|
||||
{
|
||||
lockdep_assert_held(&wiphy->mtx);
|
||||
|
||||
hrtimer_cancel(&hrwork->timer);
|
||||
wiphy_work_flush(wiphy, &hrwork->work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wiphy_hrtimer_work_flush);
|
||||
|
||||
bool wiphy_hrtimer_work_pending(struct wiphy *wiphy,
|
||||
struct wiphy_hrtimer_work *hrwork)
|
||||
{
|
||||
return hrtimer_is_queued(&hrwork->timer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wiphy_hrtimer_work_pending);
|
||||
|
||||
static int __init cfg80211_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
@@ -304,6 +304,27 @@ TRACE_EVENT(wiphy_delayed_work_queue,
|
||||
__entry->delay)
|
||||
);
|
||||
|
||||
TRACE_EVENT(wiphy_hrtimer_work_queue,
|
||||
TP_PROTO(struct wiphy *wiphy, struct wiphy_work *work,
|
||||
ktime_t delay),
|
||||
TP_ARGS(wiphy, work, delay),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
__field(void *, instance)
|
||||
__field(void *, func)
|
||||
__field(ktime_t, delay)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
__entry->instance = work;
|
||||
__entry->func = work->func;
|
||||
__entry->delay = delay;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT " instance=%p func=%pS delay=%llu",
|
||||
WIPHY_PR_ARG, __entry->instance, __entry->func,
|
||||
__entry->delay)
|
||||
);
|
||||
|
||||
TRACE_EVENT(wiphy_work_worker_start,
|
||||
TP_PROTO(struct wiphy *wiphy),
|
||||
TP_ARGS(wiphy),
|
||||
|
||||
Reference in New Issue
Block a user