1*147f7663Sriastradh /* $NetBSD: linux_hrtimer.c,v 1.3 2021/12/19 11:55:47 riastradh Exp $ */
26934fb94Sriastradh
36934fb94Sriastradh /*-
46934fb94Sriastradh * Copyright (c) 2021 The NetBSD Foundation, Inc.
56934fb94Sriastradh * All rights reserved.
66934fb94Sriastradh *
76934fb94Sriastradh * Redistribution and use in source and binary forms, with or without
86934fb94Sriastradh * modification, are permitted provided that the following conditions
96934fb94Sriastradh * are met:
106934fb94Sriastradh * 1. Redistributions of source code must retain the above copyright
116934fb94Sriastradh * notice, this list of conditions and the following disclaimer.
126934fb94Sriastradh * 2. Redistributions in binary form must reproduce the above copyright
136934fb94Sriastradh * notice, this list of conditions and the following disclaimer in the
146934fb94Sriastradh * documentation and/or other materials provided with the distribution.
156934fb94Sriastradh *
166934fb94Sriastradh * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
176934fb94Sriastradh * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
186934fb94Sriastradh * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
196934fb94Sriastradh * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
206934fb94Sriastradh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
216934fb94Sriastradh * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
226934fb94Sriastradh * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
236934fb94Sriastradh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
246934fb94Sriastradh * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
256934fb94Sriastradh * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
266934fb94Sriastradh * POSSIBILITY OF SUCH DAMAGE.
276934fb94Sriastradh */
286934fb94Sriastradh
296934fb94Sriastradh #include <sys/cdefs.h>
30*147f7663Sriastradh __KERNEL_RCSID(0, "$NetBSD: linux_hrtimer.c,v 1.3 2021/12/19 11:55:47 riastradh Exp $");
316934fb94Sriastradh
326934fb94Sriastradh #include <sys/types.h>
336934fb94Sriastradh #include <sys/callout.h>
346934fb94Sriastradh
356934fb94Sriastradh #include <linux/hrtimer.h>
366934fb94Sriastradh #include <linux/ktime.h>
376934fb94Sriastradh
386934fb94Sriastradh static void hrtimer_fire(void *);
396934fb94Sriastradh
406934fb94Sriastradh void
hrtimer_init(struct hrtimer * hrt,clockid_t clkid,enum hrtimer_mode mode)416934fb94Sriastradh hrtimer_init(struct hrtimer *hrt, clockid_t clkid, enum hrtimer_mode mode)
426934fb94Sriastradh {
436934fb94Sriastradh
446934fb94Sriastradh KASSERTMSG(clkid == CLOCK_MONOTONIC, "clkid %d", clkid);
456934fb94Sriastradh
46*147f7663Sriastradh callout_init(&hrt->hrt_ch, CALLOUT_MPSAFE);
47*147f7663Sriastradh callout_setfunc(&hrt->hrt_ch, hrtimer_fire, hrt);
48*147f7663Sriastradh hrt->hrt_mode = mode;
496934fb94Sriastradh }
506934fb94Sriastradh
516934fb94Sriastradh static void
_hrtimer_schedule(struct hrtimer * hrt)526934fb94Sriastradh _hrtimer_schedule(struct hrtimer *hrt)
536934fb94Sriastradh {
546934fb94Sriastradh int delta;
556934fb94Sriastradh
56*147f7663Sriastradh switch (hrt->hrt_mode) {
576934fb94Sriastradh case HRTIMER_MODE_ABS:
586934fb94Sriastradh panic("absolute hrtimer NYI");
596934fb94Sriastradh break;
606934fb94Sriastradh case HRTIMER_MODE_REL:
61*147f7663Sriastradh delta = ktime_to_ms(hrt->hrt_expires);
626934fb94Sriastradh break;
636934fb94Sriastradh default:
64*147f7663Sriastradh panic("invalid hrtimer mode %d", hrt->hrt_mode);
656934fb94Sriastradh }
66*147f7663Sriastradh callout_schedule(&hrt->hrt_ch, delta);
676934fb94Sriastradh }
686934fb94Sriastradh
696934fb94Sriastradh static void
hrtimer_fire(void * cookie)706934fb94Sriastradh hrtimer_fire(void *cookie)
716934fb94Sriastradh {
726934fb94Sriastradh struct hrtimer *hrt = cookie;
736934fb94Sriastradh
746934fb94Sriastradh switch ((*hrt->function)(hrt)) {
756934fb94Sriastradh case HRTIMER_RESTART:
766934fb94Sriastradh _hrtimer_schedule(hrt);
776934fb94Sriastradh break;
786934fb94Sriastradh case HRTIMER_NORESTART:
796934fb94Sriastradh break;
806934fb94Sriastradh }
816934fb94Sriastradh
82*147f7663Sriastradh callout_ack(&hrt->hrt_ch);
836934fb94Sriastradh }
846934fb94Sriastradh
856934fb94Sriastradh void
hrtimer_set_expires(struct hrtimer * hrt,ktime_t expires)866934fb94Sriastradh hrtimer_set_expires(struct hrtimer *hrt, ktime_t expires)
876934fb94Sriastradh {
886934fb94Sriastradh
89*147f7663Sriastradh hrt->hrt_expires = expires;
906934fb94Sriastradh }
916934fb94Sriastradh
926934fb94Sriastradh void
hrtimer_add_expires_ns(struct hrtimer * hrt,uint64_t ns)936934fb94Sriastradh hrtimer_add_expires_ns(struct hrtimer *hrt, uint64_t ns)
946934fb94Sriastradh {
956934fb94Sriastradh
96*147f7663Sriastradh hrt->hrt_expires = ktime_add_ns(hrt->hrt_expires, ns);
976934fb94Sriastradh }
986934fb94Sriastradh
996934fb94Sriastradh void
hrtimer_start(struct hrtimer * hrt,ktime_t expires,enum hrtimer_mode mode)1006934fb94Sriastradh hrtimer_start(struct hrtimer *hrt, ktime_t expires, enum hrtimer_mode mode)
1016934fb94Sriastradh {
1026934fb94Sriastradh
1036934fb94Sriastradh hrtimer_start_range_ns(hrt, expires, 0, mode);
1046934fb94Sriastradh }
1056934fb94Sriastradh
1066934fb94Sriastradh void
hrtimer_start_range_ns(struct hrtimer * hrt,ktime_t expires,uint64_t range_ns,enum hrtimer_mode mode)1076934fb94Sriastradh hrtimer_start_range_ns(struct hrtimer *hrt, ktime_t expires, uint64_t range_ns,
1086934fb94Sriastradh enum hrtimer_mode mode)
1096934fb94Sriastradh {
1106934fb94Sriastradh
111*147f7663Sriastradh hrt->hrt_expires = expires;
1126934fb94Sriastradh (void)range_ns;
113*147f7663Sriastradh hrt->hrt_mode = mode;
1146934fb94Sriastradh _hrtimer_schedule(hrt);
1156934fb94Sriastradh }
1166934fb94Sriastradh
1176934fb94Sriastradh int
hrtimer_cancel(struct hrtimer * hrt)1186934fb94Sriastradh hrtimer_cancel(struct hrtimer *hrt)
1196934fb94Sriastradh {
1206934fb94Sriastradh bool active;
1216934fb94Sriastradh
1226934fb94Sriastradh /*
1236934fb94Sriastradh * Halt the callout and ascertain whether the hrtimer was
1246934fb94Sriastradh * active when we invoked hrtimer_cancel.
1256934fb94Sriastradh */
126*147f7663Sriastradh if (callout_halt(&hrt->hrt_ch, NULL)) {
1276934fb94Sriastradh /* Callout expired, meaning it was active. */
1286934fb94Sriastradh active = true;
1296934fb94Sriastradh } else {
1306934fb94Sriastradh /*
1316934fb94Sriastradh * Callout had not yet expired. It will not expire
1326934fb94Sriastradh * now, so callout_pending is now stable and
1336934fb94Sriastradh * corresponds with whether the hrtimer was active or
1346934fb94Sriastradh * not.
1356934fb94Sriastradh */
136*147f7663Sriastradh active = callout_pending(&hrt->hrt_ch);
1376934fb94Sriastradh }
138c7c0494bSriastradh return active;
139c7c0494bSriastradh }
140c7c0494bSriastradh
1416934fb94Sriastradh bool
hrtimer_active(struct hrtimer * hrt)1426934fb94Sriastradh hrtimer_active(struct hrtimer *hrt)
1436934fb94Sriastradh {
1446934fb94Sriastradh
1456934fb94Sriastradh /*
1466934fb94Sriastradh * If the callout has been scheduled, but has not yet fired,
1476934fb94Sriastradh * then it is pending.
1486934fb94Sriastradh *
1496934fb94Sriastradh * If the callout has fired, but has not yet reached
1506934fb94Sriastradh * callout_ack, then it is invoking.
1516934fb94Sriastradh */
152*147f7663Sriastradh return callout_pending(&hrt->hrt_ch) || callout_invoking(&hrt->hrt_ch);
1536934fb94Sriastradh }
1546934fb94Sriastradh
1556934fb94Sriastradh uint64_t
hrtimer_forward(struct hrtimer * hrt,ktime_t now,ktime_t period)1566934fb94Sriastradh hrtimer_forward(struct hrtimer *hrt, ktime_t now, ktime_t period)
1576934fb94Sriastradh {
1586934fb94Sriastradh uint64_t now_ms, period_ms, expires_ms, nperiods;
1596934fb94Sriastradh
160*147f7663Sriastradh KASSERT(!callout_pending(&hrt->hrt_ch));
1616934fb94Sriastradh
1626934fb94Sriastradh /*
1636934fb94Sriastradh * Can't get better than 10ms precision (or ~1ms if you set
1646934fb94Sriastradh * HZ=1000) so not much point in doing this arithmetic at finer
1656934fb94Sriastradh * resolution than ms.
1666934fb94Sriastradh */
1676934fb94Sriastradh now_ms = ktime_to_ms(now);
1686934fb94Sriastradh period_ms = ktime_to_ms(period);
169*147f7663Sriastradh expires_ms = ktime_to_ms(hrt->hrt_expires);
1706934fb94Sriastradh
1716934fb94Sriastradh /* If it hasn't yet expired, no overruns. */
1726934fb94Sriastradh if (now_ms < expires_ms)
1736934fb94Sriastradh return 0;
1746934fb94Sriastradh
1756934fb94Sriastradh /* Advance it by as many periods as it should have fired. */
1766934fb94Sriastradh /* XXX fenceposts */
1776934fb94Sriastradh nperiods = howmany(now_ms - expires_ms, period_ms);
178*147f7663Sriastradh hrt->hrt_expires = ktime_add_ns(hrt->hrt_expires,
179*147f7663Sriastradh 1000000*nperiods*period_ms);
1806934fb94Sriastradh
1816934fb94Sriastradh return nperiods;
1826934fb94Sriastradh }
1836934fb94Sriastradh
1846934fb94Sriastradh uint64_t
hrtimer_forward_now(struct hrtimer * hrt,ktime_t period)1856934fb94Sriastradh hrtimer_forward_now(struct hrtimer *hrt, ktime_t period)
1866934fb94Sriastradh {
1876934fb94Sriastradh
1886934fb94Sriastradh return hrtimer_forward(hrt, ktime_get(), period);
1896934fb94Sriastradh }
190