[PATCH 7/7] TC-ETF support PTP clocks

From: Erez Geva
Date: Thu Oct 01 2020 - 17:08:26 EST


- Add support for using a POSIX dynamic clock with
Traffic control Earliest TxTime First (ETF) Qdisc.

Signed-off-by: Erez Geva <erez.geva.ext@xxxxxxxxxxx>
---
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