get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/1122375/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1122375,
    "url": "http://patchwork.ozlabs.org/api/patches/1122375/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/intel-wired-lan/patch/1561500439-30276-7-git-send-email-vedang.patel@intel.com/",
    "project": {
        "id": 46,
        "url": "http://patchwork.ozlabs.org/api/projects/46/?format=api",
        "name": "Intel Wired Ethernet development",
        "link_name": "intel-wired-lan",
        "list_id": "intel-wired-lan.osuosl.org",
        "list_email": "intel-wired-lan@osuosl.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<1561500439-30276-7-git-send-email-vedang.patel@intel.com>",
    "list_archive_url": null,
    "date": "2019-06-25T22:07:17",
    "name": "[net-next,v6,6/8] taprio: Add support for txtime-assist mode",
    "commit_ref": null,
    "pull_url": null,
    "state": "awaiting-upstream",
    "archived": false,
    "hash": "623b386d237bbb0190589c03d17da047520d4e6a",
    "submitter": {
        "id": 76208,
        "url": "http://patchwork.ozlabs.org/api/people/76208/?format=api",
        "name": "Vedang Patel",
        "email": "vedang.patel@intel.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/intel-wired-lan/patch/1561500439-30276-7-git-send-email-vedang.patel@intel.com/mbox/",
    "series": [
        {
            "id": 116115,
            "url": "http://patchwork.ozlabs.org/api/series/116115/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/intel-wired-lan/list/?series=116115",
            "date": "2019-06-25T22:07:11",
            "name": "net/sched: Add txtime-assist support for taprio.",
            "version": 6,
            "mbox": "http://patchwork.ozlabs.org/series/116115/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/1122375/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/1122375/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<intel-wired-lan-bounces@osuosl.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "intel-wired-lan@lists.osuosl.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@bilbo.ozlabs.org",
            "intel-wired-lan@lists.osuosl.org"
        ],
        "Authentication-Results": [
            "ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=osuosl.org\n\t(client-ip=140.211.166.136; helo=silver.osuosl.org;\n\tenvelope-from=intel-wired-lan-bounces@osuosl.org;\n\treceiver=<UNKNOWN>)",
            "ozlabs.org;\n\tdmarc=fail (p=none dis=none) header.from=intel.com"
        ],
        "Received": [
            "from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 45YLfL0lr9z9s4V\n\tfor <incoming@patchwork.ozlabs.org>;\n\tWed, 26 Jun 2019 08:38:17 +1000 (AEST)",
            "from localhost (localhost [127.0.0.1])\n\tby silver.osuosl.org (Postfix) with ESMTP id 772E92051B;\n\tTue, 25 Jun 2019 22:38:15 +0000 (UTC)",
            "from silver.osuosl.org ([127.0.0.1])\n\tby localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id lusZBZhI-pju; Tue, 25 Jun 2019 22:38:11 +0000 (UTC)",
            "from ash.osuosl.org (ash.osuosl.org [140.211.166.34])\n\tby silver.osuosl.org (Postfix) with ESMTP id 5604720411;\n\tTue, 25 Jun 2019 22:38:11 +0000 (UTC)",
            "from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137])\n\tby ash.osuosl.org (Postfix) with ESMTP id 9FAD71BF38E\n\tfor <intel-wired-lan@lists.osuosl.org>;\n\tTue, 25 Jun 2019 22:07:32 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n\tby fraxinus.osuosl.org (Postfix) with ESMTP id 9C47385F6C\n\tfor <intel-wired-lan@lists.osuosl.org>;\n\tTue, 25 Jun 2019 22:07:32 +0000 (UTC)",
            "from fraxinus.osuosl.org ([127.0.0.1])\n\tby localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id 8uxH6Yijvqlq for <intel-wired-lan@lists.osuosl.org>;\n\tTue, 25 Jun 2019 22:07:31 +0000 (UTC)",
            "from mga06.intel.com (mga06.intel.com [134.134.136.31])\n\tby fraxinus.osuosl.org (Postfix) with ESMTPS id 38C9385F4C\n\tfor <intel-wired-lan@lists.osuosl.org>;\n\tTue, 25 Jun 2019 22:07:31 +0000 (UTC)",
            "from orsmga002.jf.intel.com ([10.7.209.21])\n\tby orsmga104.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t25 Jun 2019 15:07:31 -0700",
            "from vpatel-desk.jf.intel.com (HELO localhost.localdomain)\n\t([10.7.159.52])\n\tby orsmga002.jf.intel.com with ESMTP; 25 Jun 2019 15:07:30 -0700"
        ],
        "X-Virus-Scanned": [
            "amavisd-new at osuosl.org",
            "amavisd-new at osuosl.org"
        ],
        "X-Greylist": "domain auto-whitelisted by SQLgrey-1.7.6",
        "X-Amp-Result": "SKIPPED(no attachment in message)",
        "X-Amp-File-Uploaded": "False",
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.63,417,1557212400\"; d=\"scan'208\";a=\"172511972\"",
        "From": "Vedang Patel <vedang.patel@intel.com>",
        "To": "netdev@vger.kernel.org",
        "Date": "Tue, 25 Jun 2019 15:07:17 -0700",
        "Message-Id": "<1561500439-30276-7-git-send-email-vedang.patel@intel.com>",
        "X-Mailer": "git-send-email 2.7.3",
        "In-Reply-To": "<1561500439-30276-1-git-send-email-vedang.patel@intel.com>",
        "References": "<1561500439-30276-1-git-send-email-vedang.patel@intel.com>",
        "X-Mailman-Approved-At": "Tue, 25 Jun 2019 22:38:08 +0000",
        "Subject": "[Intel-wired-lan] [PATCH net-next v6 6/8] taprio: Add support for\n\ttxtime-assist mode",
        "X-BeenThere": "intel-wired-lan@osuosl.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "Intel Wired Ethernet Linux Kernel Driver Development\n\t<intel-wired-lan.osuosl.org>",
        "List-Unsubscribe": "<https://lists.osuosl.org/mailman/options/intel-wired-lan>, \n\t<mailto:intel-wired-lan-request@osuosl.org?subject=unsubscribe>",
        "List-Archive": "<http://lists.osuosl.org/pipermail/intel-wired-lan/>",
        "List-Post": "<mailto:intel-wired-lan@osuosl.org>",
        "List-Help": "<mailto:intel-wired-lan-request@osuosl.org?subject=help>",
        "List-Subscribe": "<https://lists.osuosl.org/mailman/listinfo/intel-wired-lan>, \n\t<mailto:intel-wired-lan-request@osuosl.org?subject=subscribe>",
        "Cc": "jiri@resnulli.us, l@dorileo.org, sergei.shtylyov@cogentembedded.com,\n\tjakub.kicinski@netronome.com, jhs@mojatatu.com, m-karicheri2@ti.com, \n\tintel-wired-lan@lists.osuosl.org, xiyou.wangcong@gmail.com,\n\teric.dumazet@gmail.com, davem@davemloft.net",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "intel-wired-lan-bounces@osuosl.org",
        "Sender": "\"Intel-wired-lan\" <intel-wired-lan-bounces@osuosl.org>"
    },
    "content": "Currently, we are seeing non-critical packets being transmitted outside of\ntheir timeslice. We can confirm that the packets are being dequeued at the\nright time. So, the delay is induced in the hardware side.  The most likely\nreason is the hardware queues are starving the lower priority queues.\n\nIn order to improve the performance of taprio, we will be making use of the\ntxtime feature provided by the ETF qdisc. For all the packets which do not\nhave the SO_TXTIME option set, taprio will set the transmit timestamp (set\nin skb->tstamp) in this mode. TAPrio Qdisc will ensure that the transmit\ntime for the packet is set to when the gate is open. If SO_TXTIME is set,\nthe TAPrio qdisc will validate whether the timestamp (in skb->tstamp)\noccurs when the gate corresponding to skb's traffic class is open.\n\nFollowing two parameters added to support this mode:\n- flags: used to enable txtime-assist mode. Will also be used to enable\n  other modes (like hardware offloading) later.\n- txtime-delay: This indicates the minimum time it will take for the packet\n  to hit the wire. This is useful in determining whether we can transmit\nthe packet in the remaining time if the gate corresponding to the packet is\ncurrently open.\n\nAn example configuration for enabling txtime-assist:\n\ntc qdisc replace dev eth0 parent root handle 100 taprio \\\\\n      num_tc 3 \\\\\n      map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 \\\\\n      queues 1@0 1@0 1@0 \\\\\n      base-time 1558653424279842568 \\\\\n      sched-entry S 01 300000 \\\\\n      sched-entry S 02 300000 \\\\\n      sched-entry S 04 400000 \\\\\n      flags 0x1 \\\\\n      txtime-delay 40000 \\\\\n      clockid CLOCK_TAI\n\ntc qdisc replace dev $IFACE parent 100:1 etf skip_sock_check \\\\\n      offload delta 200000 clockid CLOCK_TAI\n\nNote that all the traffic classes are mapped to the same queue.  This is\nonly possible in taprio when txtime-assist is enabled. Also, note that the\nETF Qdisc is enabled with offload mode set.\n\nIn this mode, if the packet's traffic class is open and the complete packet\ncan be transmitted, taprio will try to transmit the packet immediately.\nThis will be done by setting skb->tstamp to current_time + the time delta\nindicated in the txtime-delay parameter. This parameter indicates the time\ntaken (in software) for packet to reach the network adapter.\n\nIf the packet cannot be transmitted in the current interval or if the\npacket's traffic is not currently transmitting, the skb->tstamp is set to\nthe next available timestamp value. This is tracked in the next_launchtime\nparameter in the struct sched_entry.\n\nThe behaviour w.r.t admin and oper schedules is not changed from what is\npresent in software mode.\n\nThe transmit time is already known in advance. So, we do not need the HR\ntimers to advance the schedule and wakeup the dequeue side of taprio.  So,\nHR timer won't be run when this mode is enabled.\n\nSigned-off-by: Vedang Patel <vedang.patel@intel.com>\n---\n include/uapi/linux/pkt_sched.h |   4 +\n net/sched/sch_taprio.c         | 341 +++++++++++++++++++++++++++++++++++++++--\n 2 files changed, 328 insertions(+), 17 deletions(-)",
    "diff": "diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h\nindex 127ac6d2888c..390efb54b2e0 100644\n--- a/include/uapi/linux/pkt_sched.h\n+++ b/include/uapi/linux/pkt_sched.h\n@@ -1159,6 +1159,8 @@ enum {\n  *       [TCA_TAPRIO_ATTR_SCHED_ENTRY_INTERVAL]\n  */\n \n+#define TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST 0x1\n+\n enum {\n \tTCA_TAPRIO_ATTR_UNSPEC,\n \tTCA_TAPRIO_ATTR_PRIOMAP, /* struct tc_mqprio_qopt */\n@@ -1170,6 +1172,8 @@ enum {\n \tTCA_TAPRIO_ATTR_ADMIN_SCHED, /* The admin sched, only used in dump */\n \tTCA_TAPRIO_ATTR_SCHED_CYCLE_TIME, /* s64 */\n \tTCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION, /* s64 */\n+\tTCA_TAPRIO_ATTR_FLAGS, /* u32 */\n+\tTCA_TAPRIO_ATTR_TXTIME_DELAY, /* s32 */\n \t__TCA_TAPRIO_ATTR_MAX,\n };\n \ndiff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c\nindex 6ef0cc03fdb9..078230e44471 100644\n--- a/net/sched/sch_taprio.c\n+++ b/net/sched/sch_taprio.c\n@@ -21,12 +21,16 @@\n #include <net/pkt_sched.h>\n #include <net/pkt_cls.h>\n #include <net/sch_generic.h>\n+#include <net/sock.h>\n \n static LIST_HEAD(taprio_list);\n static DEFINE_SPINLOCK(taprio_list_lock);\n \n #define TAPRIO_ALL_GATES_OPEN -1\n \n+#define FLAGS_VALID(flags) (!((flags) & ~TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST))\n+#define TXTIME_ASSIST_IS_ENABLED(flags) ((flags) & TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST)\n+\n struct sched_entry {\n \tstruct list_head list;\n \n@@ -35,6 +39,7 @@ struct sched_entry {\n \t * packet leaves after this time.\n \t */\n \tktime_t close_time;\n+\tktime_t next_txtime;\n \tatomic_t budget;\n \tint index;\n \tu32 gate_mask;\n@@ -55,6 +60,7 @@ struct sched_gate_list {\n struct taprio_sched {\n \tstruct Qdisc **qdiscs;\n \tstruct Qdisc *root;\n+\tu32 flags;\n \tint clockid;\n \tatomic64_t picos_per_byte; /* Using picoseconds because for 10Gbps+\n \t\t\t\t    * speeds it's sub-nanoseconds per byte\n@@ -68,6 +74,7 @@ struct taprio_sched {\n \tktime_t (*get_time)(void);\n \tstruct hrtimer advance_timer;\n \tstruct list_head taprio_list;\n+\tint txtime_delay;\n };\n \n static ktime_t sched_base_time(const struct sched_gate_list *sched)\n@@ -108,6 +115,227 @@ static void switch_schedules(struct taprio_sched *q,\n \t*admin = NULL;\n }\n \n+/* Get how much time has been already elapsed in the current cycle. */\n+static s32 get_cycle_time_elapsed(struct sched_gate_list *sched, ktime_t time)\n+{\n+\tktime_t time_since_sched_start;\n+\ts32 time_elapsed;\n+\n+\ttime_since_sched_start = ktime_sub(time, sched->base_time);\n+\tdiv_s64_rem(time_since_sched_start, sched->cycle_time, &time_elapsed);\n+\n+\treturn time_elapsed;\n+}\n+\n+static ktime_t get_interval_end_time(struct sched_gate_list *sched,\n+\t\t\t\t     struct sched_gate_list *admin,\n+\t\t\t\t     struct sched_entry *entry,\n+\t\t\t\t     ktime_t intv_start)\n+{\n+\ts32 cycle_elapsed = get_cycle_time_elapsed(sched, intv_start);\n+\tktime_t intv_end, cycle_ext_end, cycle_end;\n+\n+\tcycle_end = ktime_add_ns(intv_start, sched->cycle_time - cycle_elapsed);\n+\tintv_end = ktime_add_ns(intv_start, entry->interval);\n+\tcycle_ext_end = ktime_add(cycle_end, sched->cycle_time_extension);\n+\n+\tif (ktime_before(intv_end, cycle_end))\n+\t\treturn intv_end;\n+\telse if (admin && admin != sched &&\n+\t\t ktime_after(admin->base_time, cycle_end) &&\n+\t\t ktime_before(admin->base_time, cycle_ext_end))\n+\t\treturn admin->base_time;\n+\telse\n+\t\treturn cycle_end;\n+}\n+\n+static int length_to_duration(struct taprio_sched *q, int len)\n+{\n+\treturn div_u64(len * atomic64_read(&q->picos_per_byte), 1000);\n+}\n+\n+/* Returns the entry corresponding to next available interval. If\n+ * validate_interval is set, it only validates whether the timestamp occurs\n+ * when the gate corresponding to the skb's traffic class is open.\n+ */\n+static struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,\n+\t\t\t\t\t\t  struct Qdisc *sch,\n+\t\t\t\t\t\t  struct sched_gate_list *sched,\n+\t\t\t\t\t\t  struct sched_gate_list *admin,\n+\t\t\t\t\t\t  ktime_t time,\n+\t\t\t\t\t\t  ktime_t *interval_start,\n+\t\t\t\t\t\t  ktime_t *interval_end,\n+\t\t\t\t\t\t  bool validate_interval)\n+{\n+\tktime_t curr_intv_start, curr_intv_end, cycle_end, packet_transmit_time;\n+\tktime_t earliest_txtime = KTIME_MAX, txtime, cycle, transmit_end_time;\n+\tstruct sched_entry *entry = NULL, *entry_found = NULL;\n+\tstruct taprio_sched *q = qdisc_priv(sch);\n+\tstruct net_device *dev = qdisc_dev(sch);\n+\tbool entry_available = false;\n+\ts32 cycle_elapsed;\n+\tint tc, n;\n+\n+\ttc = netdev_get_prio_tc_map(dev, skb->priority);\n+\tpacket_transmit_time = length_to_duration(q, qdisc_pkt_len(skb));\n+\n+\t*interval_start = 0;\n+\t*interval_end = 0;\n+\n+\tif (!sched)\n+\t\treturn NULL;\n+\n+\tcycle = sched->cycle_time;\n+\tcycle_elapsed = get_cycle_time_elapsed(sched, time);\n+\tcurr_intv_end = ktime_sub_ns(time, cycle_elapsed);\n+\tcycle_end = ktime_add_ns(curr_intv_end, cycle);\n+\n+\tlist_for_each_entry(entry, &sched->entries, list) {\n+\t\tcurr_intv_start = curr_intv_end;\n+\t\tcurr_intv_end = get_interval_end_time(sched, admin, entry,\n+\t\t\t\t\t\t      curr_intv_start);\n+\n+\t\tif (ktime_after(curr_intv_start, cycle_end))\n+\t\t\tbreak;\n+\n+\t\tif (!(entry->gate_mask & BIT(tc)) ||\n+\t\t    packet_transmit_time > entry->interval)\n+\t\t\tcontinue;\n+\n+\t\ttxtime = entry->next_txtime;\n+\n+\t\tif (ktime_before(txtime, time) || validate_interval) {\n+\t\t\ttransmit_end_time = ktime_add_ns(time, packet_transmit_time);\n+\t\t\tif ((ktime_before(curr_intv_start, time) &&\n+\t\t\t     ktime_before(transmit_end_time, curr_intv_end)) ||\n+\t\t\t    (ktime_after(curr_intv_start, time) && !validate_interval)) {\n+\t\t\t\tentry_found = entry;\n+\t\t\t\t*interval_start = curr_intv_start;\n+\t\t\t\t*interval_end = curr_intv_end;\n+\t\t\t\tbreak;\n+\t\t\t} else if (!entry_available && !validate_interval) {\n+\t\t\t\t/* Here, we are just trying to find out the\n+\t\t\t\t * first available interval in the next cycle.\n+\t\t\t\t */\n+\t\t\t\tentry_available = 1;\n+\t\t\t\tentry_found = entry;\n+\t\t\t\t*interval_start = ktime_add_ns(curr_intv_start, cycle);\n+\t\t\t\t*interval_end = ktime_add_ns(curr_intv_end, cycle);\n+\t\t\t}\n+\t\t} else if (ktime_before(txtime, earliest_txtime) &&\n+\t\t\t   !entry_available) {\n+\t\t\tearliest_txtime = txtime;\n+\t\t\tentry_found = entry;\n+\t\t\tn = div_s64(ktime_sub(txtime, curr_intv_start), cycle);\n+\t\t\t*interval_start = ktime_add(curr_intv_start, n * cycle);\n+\t\t\t*interval_end = ktime_add(curr_intv_end, n * cycle);\n+\t\t}\n+\t}\n+\n+\treturn entry_found;\n+}\n+\n+static bool is_valid_interval(struct sk_buff *skb, struct Qdisc *sch)\n+{\n+\tstruct taprio_sched *q = qdisc_priv(sch);\n+\tstruct sched_gate_list *sched, *admin;\n+\tktime_t interval_start, interval_end;\n+\tstruct sched_entry *entry;\n+\n+\trcu_read_lock();\n+\tsched = rcu_dereference(q->oper_sched);\n+\tadmin = rcu_dereference(q->admin_sched);\n+\n+\tentry = find_entry_to_transmit(skb, sch, sched, admin, skb->tstamp,\n+\t\t\t\t       &interval_start, &interval_end, true);\n+\trcu_read_unlock();\n+\n+\treturn entry;\n+}\n+\n+/* There are a few scenarios where we will have to modify the txtime from\n+ * what is read from next_txtime in sched_entry. They are:\n+ * 1. If txtime is in the past,\n+ *    a. The gate for the traffic class is currently open and packet can be\n+ *       transmitted before it closes, schedule the packet right away.\n+ *    b. If the gate corresponding to the traffic class is going to open later\n+ *       in the cycle, set the txtime of packet to the interval start.\n+ * 2. If txtime is in the future, there are packets corresponding to the\n+ *    current traffic class waiting to be transmitted. So, the following\n+ *    possibilities exist:\n+ *    a. We can transmit the packet before the window containing the txtime\n+ *       closes.\n+ *    b. The window might close before the transmission can be completed\n+ *       successfully. So, schedule the packet in the next open window.\n+ */\n+static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)\n+{\n+\tktime_t transmit_end_time, interval_end, interval_start;\n+\tstruct taprio_sched *q = qdisc_priv(sch);\n+\tstruct sched_gate_list *sched, *admin;\n+\tktime_t minimum_time, now, txtime;\n+\tint len, packet_transmit_time;\n+\tstruct sched_entry *entry;\n+\tbool sched_changed;\n+\n+\tnow = q->get_time();\n+\tminimum_time = ktime_add_ns(now, q->txtime_delay);\n+\n+\trcu_read_lock();\n+\tadmin = rcu_dereference(q->admin_sched);\n+\tsched = rcu_dereference(q->oper_sched);\n+\tif (admin && ktime_after(minimum_time, admin->base_time))\n+\t\tswitch_schedules(q, &admin, &sched);\n+\n+\t/* Until the schedule starts, all the queues are open */\n+\tif (!sched || ktime_before(minimum_time, sched->base_time)) {\n+\t\ttxtime = minimum_time;\n+\t\tgoto done;\n+\t}\n+\n+\tlen = qdisc_pkt_len(skb);\n+\tpacket_transmit_time = length_to_duration(q, len);\n+\n+\tdo {\n+\t\tsched_changed = 0;\n+\n+\t\tentry = find_entry_to_transmit(skb, sch, sched, admin,\n+\t\t\t\t\t       minimum_time,\n+\t\t\t\t\t       &interval_start, &interval_end,\n+\t\t\t\t\t       false);\n+\t\tif (!entry) {\n+\t\t\ttxtime = 0;\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\ttxtime = entry->next_txtime;\n+\t\ttxtime = max_t(ktime_t, txtime, minimum_time);\n+\t\ttxtime = max_t(ktime_t, txtime, interval_start);\n+\n+\t\tif (admin && admin != sched &&\n+\t\t    ktime_after(txtime, admin->base_time)) {\n+\t\t\tsched = admin;\n+\t\t\tsched_changed = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\ttransmit_end_time = ktime_add(txtime, packet_transmit_time);\n+\t\tminimum_time = transmit_end_time;\n+\n+\t\t/* Update the txtime of current entry to the next time it's\n+\t\t * interval starts.\n+\t\t */\n+\t\tif (ktime_after(transmit_end_time, interval_end))\n+\t\t\tentry->next_txtime = ktime_add(interval_start, sched->cycle_time);\n+\t} while (sched_changed || ktime_after(transmit_end_time, interval_end));\n+\n+\tentry->next_txtime = transmit_end_time;\n+\n+done:\n+\trcu_read_unlock();\n+\treturn txtime;\n+}\n+\n static int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch,\n \t\t\t  struct sk_buff **to_free)\n {\n@@ -121,6 +349,15 @@ static int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch,\n \tif (unlikely(!child))\n \t\treturn qdisc_drop(skb, sch, to_free);\n \n+\tif (skb->sk && sock_flag(skb->sk, SOCK_TXTIME)) {\n+\t\tif (!is_valid_interval(skb, sch))\n+\t\t\treturn qdisc_drop(skb, sch, to_free);\n+\t} else if (TXTIME_ASSIST_IS_ENABLED(q->flags)) {\n+\t\tskb->tstamp = get_packet_txtime(skb, sch);\n+\t\tif (!skb->tstamp)\n+\t\t\treturn qdisc_drop(skb, sch, to_free);\n+\t}\n+\n \tqdisc_qstats_backlog_inc(sch, skb);\n \tsch->q.qlen++;\n \n@@ -156,6 +393,9 @@ static struct sk_buff *taprio_peek(struct Qdisc *sch)\n \t\tif (!skb)\n \t\t\tcontinue;\n \n+\t\tif (TXTIME_ASSIST_IS_ENABLED(q->flags))\n+\t\t\treturn skb;\n+\n \t\tprio = skb->priority;\n \t\ttc = netdev_get_prio_tc_map(dev, prio);\n \n@@ -168,11 +408,6 @@ static struct sk_buff *taprio_peek(struct Qdisc *sch)\n \treturn NULL;\n }\n \n-static int length_to_duration(struct taprio_sched *q, int len)\n-{\n-\treturn div_u64(len * atomic64_read(&q->picos_per_byte), 1000);\n-}\n-\n static void taprio_set_budget(struct taprio_sched *q, struct sched_entry *entry)\n {\n \tatomic_set(&entry->budget,\n@@ -216,6 +451,13 @@ static struct sk_buff *taprio_dequeue(struct Qdisc *sch)\n \t\tif (unlikely(!child))\n \t\t\tcontinue;\n \n+\t\tif (TXTIME_ASSIST_IS_ENABLED(q->flags)) {\n+\t\t\tskb = child->ops->dequeue(child);\n+\t\t\tif (!skb)\n+\t\t\t\tcontinue;\n+\t\t\tgoto skb_found;\n+\t\t}\n+\n \t\tskb = child->ops->peek(child);\n \t\tif (!skb)\n \t\t\tcontinue;\n@@ -246,6 +488,7 @@ static struct sk_buff *taprio_dequeue(struct Qdisc *sch)\n \t\tif (unlikely(!skb))\n \t\t\tgoto done;\n \n+skb_found:\n \t\tqdisc_bstats_update(sch, skb);\n \t\tqdisc_qstats_backlog_dec(sch, skb);\n \t\tsch->q.qlen--;\n@@ -522,7 +765,8 @@ static int parse_taprio_schedule(struct nlattr **tb,\n \n static int taprio_parse_mqprio_opt(struct net_device *dev,\n \t\t\t\t   struct tc_mqprio_qopt *qopt,\n-\t\t\t\t   struct netlink_ext_ack *extack)\n+\t\t\t\t   struct netlink_ext_ack *extack,\n+\t\t\t\t   u32 taprio_flags)\n {\n \tint i, j;\n \n@@ -570,6 +814,9 @@ static int taprio_parse_mqprio_opt(struct net_device *dev,\n \t\t\treturn -EINVAL;\n \t\t}\n \n+\t\tif (TXTIME_ASSIST_IS_ENABLED(taprio_flags))\n+\t\t\tcontinue;\n+\n \t\t/* Verify that the offset and counts do not overlap */\n \t\tfor (j = i + 1; j < qopt->num_tc; j++) {\n \t\t\tif (last > qopt->offset[j]) {\n@@ -700,6 +947,18 @@ static int taprio_dev_notifier(struct notifier_block *nb, unsigned long event,\n \treturn NOTIFY_DONE;\n }\n \n+static void setup_txtime(struct taprio_sched *q,\n+\t\t\t struct sched_gate_list *sched, ktime_t base)\n+{\n+\tstruct sched_entry *entry;\n+\tu32 interval = 0;\n+\n+\tlist_for_each_entry(entry, &sched->entries, list) {\n+\t\tentry->next_txtime = ktime_add_ns(base, interval);\n+\t\tinterval += entry->interval;\n+\t}\n+}\n+\n static int taprio_change(struct Qdisc *sch, struct nlattr *opt,\n \t\t\t struct netlink_ext_ack *extack)\n {\n@@ -708,6 +967,7 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,\n \tstruct taprio_sched *q = qdisc_priv(sch);\n \tstruct net_device *dev = qdisc_dev(sch);\n \tstruct tc_mqprio_qopt *mqprio = NULL;\n+\tu32 taprio_flags = 0;\n \tint i, err, clockid;\n \tunsigned long flags;\n \tktime_t start;\n@@ -720,7 +980,21 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,\n \tif (tb[TCA_TAPRIO_ATTR_PRIOMAP])\n \t\tmqprio = nla_data(tb[TCA_TAPRIO_ATTR_PRIOMAP]);\n \n-\terr = taprio_parse_mqprio_opt(dev, mqprio, extack);\n+\tif (tb[TCA_TAPRIO_ATTR_FLAGS]) {\n+\t\ttaprio_flags = nla_get_u32(tb[TCA_TAPRIO_ATTR_FLAGS]);\n+\n+\t\tif (q->flags != 0 && q->flags != taprio_flags) {\n+\t\t\tNL_SET_ERR_MSG_MOD(extack, \"Changing 'flags' of a running schedule is not supported\");\n+\t\t\treturn -EOPNOTSUPP;\n+\t\t} else if (!FLAGS_VALID(taprio_flags)) {\n+\t\t\tNL_SET_ERR_MSG_MOD(extack, \"Specified 'flags' are not valid\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tq->flags = taprio_flags;\n+\t}\n+\n+\terr = taprio_parse_mqprio_opt(dev, mqprio, extack, taprio_flags);\n \tif (err < 0)\n \t\treturn err;\n \n@@ -779,7 +1053,18 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,\n \t/* Protects against enqueue()/dequeue() */\n \tspin_lock_bh(qdisc_lock(sch));\n \n-\tif (!hrtimer_active(&q->advance_timer)) {\n+\tif (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {\n+\t\tif (!TXTIME_ASSIST_IS_ENABLED(q->flags)) {\n+\t\t\tNL_SET_ERR_MSG_MOD(extack, \"txtime-delay can only be set when txtime-assist mode is enabled\");\n+\t\t\terr = -EINVAL;\n+\t\t\tgoto unlock;\n+\t\t}\n+\n+\t\tq->txtime_delay = nla_get_s32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]);\n+\t}\n+\n+\tif (!TXTIME_ASSIST_IS_ENABLED(taprio_flags) &&\n+\t    !hrtimer_active(&q->advance_timer)) {\n \t\thrtimer_init(&q->advance_timer, q->clockid, HRTIMER_MODE_ABS);\n \t\tq->advance_timer.function = advance_sched;\n \t}\n@@ -822,20 +1107,35 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,\n \t\tgoto unlock;\n \t}\n \n-\tsetup_first_close_time(q, new_admin, start);\n+\tif (TXTIME_ASSIST_IS_ENABLED(taprio_flags)) {\n+\t\tsetup_txtime(q, new_admin, start);\n \n-\t/* Protects against advance_sched() */\n-\tspin_lock_irqsave(&q->current_entry_lock, flags);\n+\t\tif (!oper) {\n+\t\t\trcu_assign_pointer(q->oper_sched, new_admin);\n+\t\t\terr = 0;\n+\t\t\tnew_admin = NULL;\n+\t\t\tgoto unlock;\n+\t\t}\n \n-\ttaprio_start_sched(sch, start, new_admin);\n+\t\trcu_assign_pointer(q->admin_sched, new_admin);\n+\t\tif (admin)\n+\t\t\tcall_rcu(&admin->rcu, taprio_free_sched_cb);\n+\t} else {\n+\t\tsetup_first_close_time(q, new_admin, start);\n \n-\trcu_assign_pointer(q->admin_sched, new_admin);\n-\tif (admin)\n-\t\tcall_rcu(&admin->rcu, taprio_free_sched_cb);\n-\tnew_admin = NULL;\n+\t\t/* Protects against advance_sched() */\n+\t\tspin_lock_irqsave(&q->current_entry_lock, flags);\n+\n+\t\ttaprio_start_sched(sch, start, new_admin);\n \n-\tspin_unlock_irqrestore(&q->current_entry_lock, flags);\n+\t\trcu_assign_pointer(q->admin_sched, new_admin);\n+\t\tif (admin)\n+\t\t\tcall_rcu(&admin->rcu, taprio_free_sched_cb);\n \n+\t\tspin_unlock_irqrestore(&q->current_entry_lock, flags);\n+\t}\n+\n+\tnew_admin = NULL;\n \terr = 0;\n \n unlock:\n@@ -1073,6 +1373,13 @@ static int taprio_dump(struct Qdisc *sch, struct sk_buff *skb)\n \tif (nla_put_s32(skb, TCA_TAPRIO_ATTR_SCHED_CLOCKID, q->clockid))\n \t\tgoto options_error;\n \n+\tif (q->flags && nla_put_u32(skb, TCA_TAPRIO_ATTR_FLAGS, q->flags))\n+\t\tgoto options_error;\n+\n+\tif (q->txtime_delay &&\n+\t    nla_put_s32(skb, TCA_TAPRIO_ATTR_TXTIME_DELAY, q->txtime_delay))\n+\t\tgoto options_error;\n+\n \tif (oper && dump_schedule(skb, oper))\n \t\tgoto options_error;\n \n",
    "prefixes": [
        "net-next",
        "v6",
        "6/8"
    ]
}