Merge branch 'misc.compat' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[sfrench/cifs-2.6.git] / net / socket.c
index af33d929135a5ebd4dab4747611bf77dc30cf8db..bf2122691fba25ae778d6fd68a179b1e9163defe 100644 (file)
@@ -461,7 +461,7 @@ EXPORT_SYMBOL(sock_from_file);
  *     @err: pointer to an error code return
  *
  *     The file handle passed in is locked and the socket it is bound
- *     too is returned. If an error occurs the err pointer is overwritten
+ *     to is returned. If an error occurs the err pointer is overwritten
  *     with a negative errno code and NULL is returned. The function checks
  *     for both invalid handles and passing a handle which is not a socket.
  *
@@ -662,6 +662,40 @@ static bool skb_is_err_queue(const struct sk_buff *skb)
        return skb->pkt_type == PACKET_OUTGOING;
 }
 
+/* On transmit, software and hardware timestamps are returned independently.
+ * As the two skb clones share the hardware timestamp, which may be updated
+ * before the software timestamp is received, a hardware TX timestamp may be
+ * returned only if there is no software TX timestamp. Ignore false software
+ * timestamps, which may be made in the __sock_recv_timestamp() call when the
+ * option SO_TIMESTAMP(NS) is enabled on the socket, even when the skb has a
+ * hardware timestamp.
+ */
+static bool skb_is_swtx_tstamp(const struct sk_buff *skb, int false_tstamp)
+{
+       return skb->tstamp && !false_tstamp && skb_is_err_queue(skb);
+}
+
+static void put_ts_pktinfo(struct msghdr *msg, struct sk_buff *skb)
+{
+       struct scm_ts_pktinfo ts_pktinfo;
+       struct net_device *orig_dev;
+
+       if (!skb_mac_header_was_set(skb))
+               return;
+
+       memset(&ts_pktinfo, 0, sizeof(ts_pktinfo));
+
+       rcu_read_lock();
+       orig_dev = dev_get_by_napi_id(skb_napi_id(skb));
+       if (orig_dev)
+               ts_pktinfo.if_index = orig_dev->ifindex;
+       rcu_read_unlock();
+
+       ts_pktinfo.pkt_length = skb->len - skb_mac_offset(skb);
+       put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPING_PKTINFO,
+                sizeof(ts_pktinfo), &ts_pktinfo);
+}
+
 /*
  * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP)
  */
@@ -670,14 +704,16 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
 {
        int need_software_tstamp = sock_flag(sk, SOCK_RCVTSTAMP);
        struct scm_timestamping tss;
-       int empty = 1;
+       int empty = 1, false_tstamp = 0;
        struct skb_shared_hwtstamps *shhwtstamps =
                skb_hwtstamps(skb);
 
        /* Race occurred between timestamp enabling and packet
           receiving.  Fill in the current time for now. */
-       if (need_software_tstamp && skb->tstamp == 0)
+       if (need_software_tstamp && skb->tstamp == 0) {
                __net_timestamp(skb);
+               false_tstamp = 1;
+       }
 
        if (need_software_tstamp) {
                if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
@@ -699,8 +735,13 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
                empty = 0;
        if (shhwtstamps &&
            (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) &&
-           ktime_to_timespec_cond(shhwtstamps->hwtstamp, tss.ts + 2))
+           !skb_is_swtx_tstamp(skb, false_tstamp) &&
+           ktime_to_timespec_cond(shhwtstamps->hwtstamp, tss.ts + 2)) {
                empty = 0;
+               if ((sk->sk_tsflags & SOF_TIMESTAMPING_OPT_PKTINFO) &&
+                   !skb_is_err_queue(skb))
+                       put_ts_pktinfo(msg, skb);
+       }
        if (!empty) {
                put_cmsg(msg, SOL_SOCKET,
                         SCM_TIMESTAMPING, sizeof(tss), &tss);
@@ -950,8 +991,7 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
                        err = -EFAULT;
                        if (get_user(pid, (int __user *)argp))
                                break;
-                       f_setown(sock->file, pid, 1);
-                       err = 0;
+                       err = f_setown(sock->file, pid, 1);
                        break;
                case FIOGETOWN:
                case SIOCGPGRP: