net/sched: Make etf report drops on error_queue
authorJesus Sanchez-Palencia <jesus.sanchez-palencia@intel.com>
Tue, 3 Jul 2018 22:43:00 +0000 (15:43 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 4 Jul 2018 13:30:28 +0000 (22:30 +0900)
Use the socket error queue for reporting dropped packets if the
socket has enabled that feature through the SO_TXTIME API.

Packets are dropped either on enqueue() if they aren't accepted by the
qdisc or on dequeue() if the system misses their deadline. Those are
reported as different errors so applications can react accordingly.

Userspace can retrieve the errors through the socket error queue and the
corresponding cmsg interfaces. A struct sock_extended_err* is used for
returning the error data, and the packet's timestamp can be retrieved by
adding both ee_data and ee_info fields as e.g.:

    ((__u64) serr->ee_data << 32) + serr->ee_info

This feature is disabled by default and must be explicitly enabled by
applications. Enabling it can bring some overhead for the Tx cycles
of the application.

Signed-off-by: Jesus Sanchez-Palencia <jesus.sanchez-palencia@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sock.h
include/uapi/linux/errqueue.h
include/uapi/linux/net_tstamp.h
net/core/sock.c
net/sched/sch_etf.c

index 68347b9821c66dbdc9194ec3a3ec44a95cc979da..e0eac9ef44b53ea52a8a3c898e3d45acdb08b3c3 100644 (file)
@@ -481,7 +481,8 @@ struct sock {
 
        u8                      sk_clockid;
        u8                      sk_txtime_deadline_mode : 1,
-                               sk_txtime_unused : 7;
+                               sk_txtime_report_errors : 1,
+                               sk_txtime_unused : 6;
 
        struct socket           *sk_socket;
        void                    *sk_user_data;
index dc64cfaf13da08564a8271e50a4edb89d221b148..c0151200f7d1cf65e961a69e3b4fca23a9ff4785 100644 (file)
@@ -20,12 +20,16 @@ struct sock_extended_err {
 #define SO_EE_ORIGIN_ICMP6     3
 #define SO_EE_ORIGIN_TXSTATUS  4
 #define SO_EE_ORIGIN_ZEROCOPY  5
+#define SO_EE_ORIGIN_TXTIME    6
 #define SO_EE_ORIGIN_TIMESTAMPING SO_EE_ORIGIN_TXSTATUS
 
 #define SO_EE_OFFENDER(ee)     ((struct sockaddr*)((ee)+1))
 
 #define SO_EE_CODE_ZEROCOPY_COPIED     1
 
+#define SO_EE_CODE_TXTIME_INVALID_PARAM        1
+#define SO_EE_CODE_TXTIME_MISSED       2
+
 /**
  *     struct scm_timestamping - timestamps exposed through cmsg
  *
index c9a77c353b98b4228217021d399a2813c1059bd6..f8f4539f11353616b0c4159319b66c788ce115be 100644 (file)
@@ -147,8 +147,11 @@ struct scm_ts_pktinfo {
  */
 enum txtime_flags {
        SOF_TXTIME_DEADLINE_MODE = (1 << 0),
+       SOF_TXTIME_REPORT_ERRORS = (1 << 1),
 
-       SOF_TXTIME_FLAGS_MASK = (SOF_TXTIME_DEADLINE_MODE)
+       SOF_TXTIME_FLAGS_LAST = SOF_TXTIME_REPORT_ERRORS,
+       SOF_TXTIME_FLAGS_MASK = (SOF_TXTIME_FLAGS_LAST - 1) |
+                                SOF_TXTIME_FLAGS_LAST
 };
 
 struct sock_txtime {
index fe64b839f1b2a3337448feaab40cf8ad4f20e9a7..03fdea5b0f575945a58fd14b546226d61ccd4988 100644 (file)
@@ -1087,6 +1087,8 @@ set_rcvbuf:
                        sk->sk_clockid = sk_txtime.clockid;
                        sk->sk_txtime_deadline_mode =
                                !!(sk_txtime.flags & SOF_TXTIME_DEADLINE_MODE);
+                       sk->sk_txtime_report_errors =
+                               !!(sk_txtime.flags & SOF_TXTIME_REPORT_ERRORS);
                }
                break;
 
@@ -1429,6 +1431,8 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                v.txtime.clockid = sk->sk_clockid;
                v.txtime.flags |= sk->sk_txtime_deadline_mode ?
                                  SOF_TXTIME_DEADLINE_MODE : 0;
+               v.txtime.flags |= sk->sk_txtime_report_errors ?
+                                 SOF_TXTIME_REPORT_ERRORS : 0;
                break;
 
        default:
index 932a136db5680307dbfc11efd966fe7d5ff240a4..1538d6fa81652023eb774c9c603ec791e87a3de3 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/errno.h>
+#include <linux/errqueue.h>
 #include <linux/rbtree.h>
 #include <linux/skbuff.h>
 #include <linux/posix-timers.h>
@@ -123,6 +124,32 @@ static void reset_watchdog(struct Qdisc *sch)
        qdisc_watchdog_schedule_ns(&q->watchdog, ktime_to_ns(next));
 }
 
+static void report_sock_error(struct sk_buff *skb, u32 err, u8 code)
+{
+       struct sock_exterr_skb *serr;
+       struct sk_buff *clone;
+       ktime_t txtime = skb->tstamp;
+
+       if (!skb->sk || !(skb->sk->sk_txtime_report_errors))
+               return;
+
+       clone = skb_clone(skb, GFP_ATOMIC);
+       if (!clone)
+               return;
+
+       serr = SKB_EXT_ERR(clone);
+       serr->ee.ee_errno = err;
+       serr->ee.ee_origin = SO_EE_ORIGIN_TXTIME;
+       serr->ee.ee_type = 0;
+       serr->ee.ee_code = code;
+       serr->ee.ee_pad = 0;
+       serr->ee.ee_data = (txtime >> 32); /* high part of tstamp */
+       serr->ee.ee_info = txtime; /* low part of tstamp */
+
+       if (sock_queue_err_skb(skb->sk, clone))
+               kfree_skb(clone);
+}
+
 static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
                                      struct sk_buff **to_free)
 {
@@ -130,8 +157,11 @@ static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
        struct rb_node **p = &q->head.rb_node, *parent = NULL;
        ktime_t txtime = nskb->tstamp;
 
-       if (!is_packet_valid(sch, nskb))
+       if (!is_packet_valid(sch, nskb)) {
+               report_sock_error(nskb, EINVAL,
+                                 SO_EE_CODE_TXTIME_INVALID_PARAM);
                return qdisc_drop(nskb, sch, to_free);
+       }
 
        while (*p) {
                struct sk_buff *skb;
@@ -174,6 +204,8 @@ static void timesortedlist_erase(struct Qdisc *sch, struct sk_buff *skb,
        if (drop) {
                struct sk_buff *to_free = NULL;
 
+               report_sock_error(skb, ECANCELED, SO_EE_CODE_TXTIME_MISSED);
+
                qdisc_drop(skb, sch, &to_free);
                kfree_skb_list(to_free);
                qdisc_qstats_overlimit(sch);
@@ -199,7 +231,6 @@ static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch)
        now = q->get_time();
 
        /* Drop if packet has expired while in queue. */
-       /* FIXME: Must return error on the socket's error queue */
        if (ktime_before(skb->tstamp, now)) {
                timesortedlist_erase(sch, skb, true);
                skb = NULL;