Commit 7ceba45a authored by Benjamin Berg's avatar Benjamin Berg Committed by Johannes Berg
Browse files

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: default avatarBenjamin Berg <benjamin.berg@intel.com>
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20251028125710.7f13a2adc5eb.I01b5af0363869864b0580d9c2a1770bafab69566@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 3b8694e5
Loading
Loading
Loading
Loading
+78 −0
Original line number Diff line number Diff line
@@ -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
 *
+56 −0
Original line number Diff line number Diff line
@@ -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;
+21 −0
Original line number Diff line number Diff line
@@ -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),