- Add support for using a POSIX dynamic clock with Traffic control Earliest TxTime First (ETF) Qdisc.
Signed-off-by: Erez Geva <erez.geva....@siemens.com> --- include/uapi/linux/net_tstamp.h | 5 ++ net/sched/sch_etf.c | 100 +++++++++++++++++++++++++++++--- 2 files changed, 97 insertions(+), 8 deletions(-) diff --git a/include/uapi/linux/net_tstamp.h b/include/uapi/linux/net_tstamp.h index 7ed0b3d1c00a..ecbaac6230d7 100644 --- a/include/uapi/linux/net_tstamp.h +++ b/include/uapi/linux/net_tstamp.h @@ -167,6 +167,11 @@ enum txtime_flags { SOF_TXTIME_FLAGS_MASK = (SOF_TXTIME_FLAGS_LAST - 1) | SOF_TXTIME_FLAGS_LAST }; +/* + * Clock ID to use with POSIX clocks + * The ID must be u8 to fit in (struct sock)->sk_clockid + */ +#define SOF_TXTIME_POSIX_CLOCK_ID (0x77) struct sock_txtime { __kernel_clockid_t clockid;/* reference clockid */ diff --git a/net/sched/sch_etf.c b/net/sched/sch_etf.c index c0de4c6f9299..8e3e0a61fa58 100644 --- a/net/sched/sch_etf.c +++ b/net/sched/sch_etf.c @@ -15,6 +15,7 @@ #include <linux/rbtree.h> #include <linux/skbuff.h> #include <linux/posix-timers.h> +#include <linux/posix-clock.h> #include <net/netlink.h> #include <net/sch_generic.h> #include <net/pkt_sched.h> @@ -40,19 +41,40 @@ struct etf_sched_data { struct rb_root_cached head; struct qdisc_watchdog watchdog; ktime_t (*get_time)(void); +#ifdef CONFIG_POSIX_TIMERS + struct posix_clock *pclock; /* pointer to a posix clock */ +#endif /* CONFIG_POSIX_TIMERS */ }; static const struct nla_policy etf_policy[TCA_ETF_MAX + 1] = { [TCA_ETF_PARMS] = { .len = sizeof(struct tc_etf_qopt) }, }; +static inline ktime_t get_now(struct Qdisc *sch, struct etf_sched_data *q) +{ +#ifdef CONFIG_POSIX_TIMERS + if (IS_ERR_OR_NULL(q->get_time)) { + struct timespec64 ts; + int err = posix_clock_gettime(q->pclock, &ts); + + if (err) { + pr_warn("Clock is disabled (%d) for queue %d\n", + err, q->queue); + return 0; + } + return timespec64_to_ktime(ts); + } +#endif /* CONFIG_POSIX_TIMERS */ + return q->get_time(); +} + static inline int validate_input_params(struct tc_etf_qopt *qopt, struct netlink_ext_ack *extack) { /* Check if params comply to the following rules: * * Clockid and delta must be valid. * - * * Dynamic clockids are not supported. + * * Dynamic CPU clockids are not supported. * * * Delta must be a positive or zero integer. * @@ -60,11 +82,22 @@ static inline int validate_input_params(struct tc_etf_qopt *qopt, * expect that system clocks have been synchronized to PHC. */ if (qopt->clockid < 0) { +#ifdef CONFIG_POSIX_TIMERS + /** + * Use of PTP clock through a posix clock. + * The TC application must open the posix clock device file + * and use the dynamic clockid from the file description. + */ + if (!is_clockid_fd_clock(qopt->clockid)) { + NL_SET_ERR_MSG(extack, + "Dynamic CPU clockids are not supported"); + return -EOPNOTSUPP; + } +#else /* CONFIG_POSIX_TIMERS */ NL_SET_ERR_MSG(extack, "Dynamic clockids are not supported"); return -ENOTSUPP; - } - - if (qopt->clockid != CLOCK_TAI) { +#endif /* CONFIG_POSIX_TIMERS */ + } else if (qopt->clockid != CLOCK_TAI) { NL_SET_ERR_MSG(extack, "Invalid clockid. CLOCK_TAI must be used"); return -EINVAL; } @@ -103,7 +136,7 @@ static bool is_packet_valid(struct Qdisc *sch, struct etf_sched_data *q, return false; skip: - now = q->get_time(); + now = get_now(sch, q); if (ktime_before(txtime, now) || ktime_before(txtime, q->last)) return false; @@ -133,6 +166,27 @@ static void reset_watchdog(struct Qdisc *sch, struct etf_sched_data *q) } next = ktime_sub_ns(skb->tstamp, q->delta + NET_SCH_ETF_TIMER_RANGE); +#ifdef CONFIG_POSIX_TIMERS + if (!IS_ERR_OR_NULL(q->pclock)) { + s64 now; + struct timespec64 ts; + int err = posix_clock_gettime(q->pclock, &ts); + + if (err) { + pr_warn("Clock is disabled (%d) for queue %d\n", + err, q->queue); + return; + } + now = timespec64_to_ns(&ts); + if (now == 0) { + pr_warn("Clock is not running for queue %d\n", + q->queue); + return; + } + /* Convert PHC to CLOCK_TAI as that is the timer clock */ + next = ktime_add(next, ktime_sub_ns(ktime_get_clocktai(), now)); + } +#endif /* CONFIG_POSIX_TIMERS */ qdisc_watchdog_schedule_soon_ns(&q->watchdog, ktime_to_ns(next), NET_SCH_ETF_TIMER_RANGE); } @@ -263,7 +317,7 @@ static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch) if (!skb) return NULL; - now = q->get_time(); + now = get_now(sch, q); /* Drop if packet has expired while in queue. */ if (ktime_before(skb->tstamp, now)) { @@ -354,6 +408,7 @@ static int etf_init(struct Qdisc *sch, struct nlattr *opt, struct nlattr *tb[TCA_ETF_MAX + 1]; struct tc_etf_qopt *qopt; int err; + clockid_t clockid; if (!opt) { NL_SET_ERR_MSG(extack, @@ -391,13 +446,17 @@ static int etf_init(struct Qdisc *sch, struct nlattr *opt, } /* Everything went OK, save the parameters used. */ + clockid = qopt->clockid; q->delta = qopt->delta; q->clockid = qopt->clockid; q->offload = OFFLOAD_IS_ON(qopt); q->deadline_mode = DEADLINE_MODE_IS_ON(qopt); q->skip_sock_check = SKIP_SOCK_CHECK_IS_SET(qopt); +#ifdef CONFIG_POSIX_TIMERS + q->pclock = NULL; +#endif /* CONFIG_POSIX_TIMERS */ - switch (q->clockid) { + switch (clockid) { case CLOCK_REALTIME: q->get_time = ktime_get_real; break; @@ -411,11 +470,31 @@ static int etf_init(struct Qdisc *sch, struct nlattr *opt, q->get_time = ktime_get_clocktai; break; default: +#ifdef CONFIG_POSIX_TIMERS + q->pclock = posix_clock_get_clock(clockid); + if (IS_ERR_OR_NULL(q->pclock)) { + NL_SET_ERR_MSG(extack, "Clockid does not exist"); + return PTR_ERR(q->pclock); + } + q->clockid = SOF_TXTIME_POSIX_CLOCK_ID; + /** + * Posix clocks do not support high-resolution timers + * Using the TAI clock is close enough (for relative sleeps) + */ + clockid = CLOCK_TAI; + /** + * Posix clock do not provide direct ktime function + * We need to call posix_clock_gettime() + */ + q->get_time = NULL; + break; +#else /* CONFIG_POSIX_TIMERS */ NL_SET_ERR_MSG(extack, "Clockid is not supported"); return -ENOTSUPP; +#endif /* CONFIG_POSIX_TIMERS */ } - qdisc_watchdog_init_clockid(&q->watchdog, sch, q->clockid); + qdisc_watchdog_init_clockid(&q->watchdog, sch, clockid); return 0; } @@ -463,6 +542,11 @@ static void etf_destroy(struct Qdisc *sch) qdisc_watchdog_cancel(&q->watchdog); etf_disable_offload(dev, q); +#ifdef CONFIG_POSIX_TIMERS + /* Release posix clock */ + if (!IS_ERR_OR_NULL(q->pclock)) + posix_clock_put_clock(q->pclock); +#endif /* CONFIG_POSIX_TIMERS */ } static int etf_dump(struct Qdisc *sch, struct sk_buff *skb) -- 2.20.1