diff mbox series

[7/8] socket: Add SO_TIMESTAMP[NS]_NEW

Message ID 20181124022035.17519-8-deepa.kernel@gmail.com
State Not Applicable
Delegated to: David Miller
Headers show
Series net: y2038-safe socket timestamps | expand

Commit Message

Deepa Dinamani Nov. 24, 2018, 2:20 a.m. UTC
Add SO_TIMESTAMP_NEW and SO_TIMESTAMPNS_NEW variants of
socket timestamp options.
These are the y2038 safe versions of the SO_TIMESTAMP_OLD
and SO_TIMESTAMPNS_OLD for all architectures.

Note that the format of scm_timestamping.ts[0] is not changed
in this patch.

Signed-off-by: Deepa Dinamani <deepa.kernel@gmail.com>
Cc: jejb@parisc-linux.org
Cc: ralf@linux-mips.org
Cc: rth@twiddle.net
Cc: linux-alpha@vger.kernel.org
Cc: linux-mips@linux-mips.org
Cc: linux-parisc@vger.kernel.org
Cc: linux-rdma@vger.kernel.org
Cc: netdev@vger.kernel.org
Cc: sparclinux@vger.kernel.org
---
 arch/alpha/include/uapi/asm/socket.h  | 15 ++++++-
 arch/mips/include/uapi/asm/socket.h   | 14 +++++-
 arch/parisc/include/uapi/asm/socket.h | 14 +++++-
 arch/sparc/include/uapi/asm/socket.h  | 14 +++++-
 include/linux/skbuff.h                | 18 ++++++++
 include/net/sock.h                    |  1 +
 include/uapi/asm-generic/socket.h     | 15 ++++++-
 net/core/sock.c                       | 18 ++++++++
 net/ipv4/tcp.c                        | 61 +++++++++++++++++++--------
 net/rds/af_rds.c                      |  8 +++-
 net/rds/recv.c                        | 16 ++++++-
 net/socket.c                          | 47 +++++++++++++++------
 12 files changed, 197 insertions(+), 44 deletions(-)

Comments

Willem de Bruijn Nov. 25, 2018, 3:59 a.m. UTC | #1
On Sat, Nov 24, 2018 at 3:58 AM Deepa Dinamani <deepa.kernel@gmail.com> wrote:
>
> Add SO_TIMESTAMP_NEW and SO_TIMESTAMPNS_NEW variants of
> socket timestamp options.
> These are the y2038 safe versions of the SO_TIMESTAMP_OLD
> and SO_TIMESTAMPNS_OLD for all architectures.
>
> Note that the format of scm_timestamping.ts[0] is not changed
> in this patch.
>
> Signed-off-by: Deepa Dinamani <deepa.kernel@gmail.com>
> Cc: jejb@parisc-linux.org
> Cc: ralf@linux-mips.org
> Cc: rth@twiddle.net
> Cc: linux-alpha@vger.kernel.org
> Cc: linux-mips@linux-mips.org
> Cc: linux-parisc@vger.kernel.org
> Cc: linux-rdma@vger.kernel.org
> Cc: netdev@vger.kernel.org
> Cc: sparclinux@vger.kernel.org
> ---

> diff --git a/include/net/sock.h b/include/net/sock.h
> index 8143c4c1a49d..9edf909dc176 100644
> --- a/include/net/sock.h
> +++ b/include/net/sock.h
> @@ -801,6 +801,7 @@ enum sock_flags {
>         SOCK_RCU_FREE, /* wait rcu grace period in sk_destruct() */
>         SOCK_TXTIME,
>         SOCK_XDP, /* XDP is attached */
> +       SOCK_TSTAMP_NEW, /* Indicates 64 bit timestamps always */

sk_flags is getting exhausted. Commit b9f40e21ef42 ("net-timestamp:
move timestamp flags out of sk_flags") added a new u16 sk_tsflags
specifically for timestamps. That may be a better choice here, too.

> diff --git a/net/core/sock.c b/net/core/sock.c
> index e60036618205..7b485dfaa400 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -652,15 +652,23 @@ static void setsockopt_timestamp(struct sock *sk, int type, int val)
>         if (!val) {
>                 sock_reset_flag(sk, SOCK_RCVTSTAMP);
>                 sock_reset_flag(sk, SOCK_RCVTSTAMPNS);
> +               sock_reset_flag(sk, SOCK_TSTAMP_NEW);
>                 return;
>         }
>
> +       if (type == SO_TIMESTAMP_NEW || type == SO_TIMESTAMPNS_NEW)
> +               sock_set_flag(sk, SOCK_TSTAMP_NEW);
> +       else
> +               sock_reset_flag(sk, SOCK_TSTAMP_NEW);
> +

if adding a boolean whether the socket uses new or old-style
timestamps, perhaps fail hard if a process tries to set a new-style
option while an old-style is already set and vice versa. Also include
SO_TIMESTAMPING_NEW as it toggles the same option.

> diff --git a/net/socket.c b/net/socket.c
> index d3defba55547..9abeb6bc9cfe 100644
> --- a/net/socket.c
> +++ b/net/socket.c
> @@ -699,6 +699,38 @@ static void put_ts_pktinfo(struct msghdr *msg, struct sk_buff *skb)
>                  sizeof(ts_pktinfo), &ts_pktinfo);
>  }
>
> +static void sock_recv_sw_timestamp(struct msghdr *msg, struct sock *sk,
> +                                  struct sk_buff *skb)
> +{
> +       if (sock_flag(sk, SOCK_TSTAMP_NEW)) {
> +               if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
> +                       struct sock_timeval tv;
> +
> +                       skb_get_new_timestamp(skb, &tv);
> +                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_NEW,
> +                                sizeof(tv), &tv);
> +               } else {
> +                       struct __kernel_timespec ts;
> +
> +                       skb_get_new_timestampns(skb, &ts);
> +                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_NEW,
> +                                sizeof(ts), &ts);
> +               }
> +       }
> +       if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
> +               struct __kernel_old_timeval tv;
> +
> +               skb_get_timestamp(skb, &tv);
> +               put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
> +                        sizeof(tv), &tv);
> +       } else {
> +               struct timespec ts;
> +
> +               skb_get_timestampns(skb, &ts);
> +               put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
> +                        sizeof(ts), &ts);
> +       }
> +}
>  /*
>   * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP)
>   * or sock_flag(sk, SOCK_RCVTSTAMPNS)
> @@ -719,19 +751,8 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
>                 false_tstamp = 1;
>         }
> -       if (need_software_tstamp) {

Considerably less code churn if adding __sock_recv_timestamp_2038 and
calling that here:

                   if (sock_flag(sk, SOCK_TSTAMP_NEW))
                           __sock_recv_timestamp_2038(msg, sk, skb);
                   else if ...

Same for the tcp case above, really, and in the case of the next patch
for SO_TIMESTAMPING_NEW


> -               if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
> -                       struct __kernel_old_timeval tv;
> -                       skb_get_timestamp(skb, &tv);
> -                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
> -                                sizeof(tv), &tv);
> -               } else {
> -                       struct timespec ts;
> -                       skb_get_timestampns(skb, &ts);
> -                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
> -                                sizeof(ts), &ts);
> -               }
> -       }


> +       if (need_software_tstamp)
> +               sock_recv_sw_timestamp(msg, sk, skb);
>
>         memset(&tss, 0, sizeof(tss));
>         if ((sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) &&
> --
> 2.17.1
>
Willem de Bruijn Nov. 25, 2018, 4:17 a.m. UTC | #2
On Sat, Nov 24, 2018 at 10:59 PM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> On Sat, Nov 24, 2018 at 3:58 AM Deepa Dinamani <deepa.kernel@gmail.com> wrote:
> >
> > Add SO_TIMESTAMP_NEW and SO_TIMESTAMPNS_NEW variants of
> > socket timestamp options.
> > These are the y2038 safe versions of the SO_TIMESTAMP_OLD
> > and SO_TIMESTAMPNS_OLD for all architectures.
> >
> > Note that the format of scm_timestamping.ts[0] is not changed
> > in this patch.
> >
> > Signed-off-by: Deepa Dinamani <deepa.kernel@gmail.com>
> > Cc: jejb@parisc-linux.org
> > Cc: ralf@linux-mips.org
> > Cc: rth@twiddle.net
> > Cc: linux-alpha@vger.kernel.org
> > Cc: linux-mips@linux-mips.org
> > Cc: linux-parisc@vger.kernel.org
> > Cc: linux-rdma@vger.kernel.org
> > Cc: netdev@vger.kernel.org
> > Cc: sparclinux@vger.kernel.org
> > ---
>
> > diff --git a/include/net/sock.h b/include/net/sock.h
> > index 8143c4c1a49d..9edf909dc176 100644
> > --- a/include/net/sock.h
> > +++ b/include/net/sock.h
> > @@ -801,6 +801,7 @@ enum sock_flags {
> >         SOCK_RCU_FREE, /* wait rcu grace period in sk_destruct() */
> >         SOCK_TXTIME,
> >         SOCK_XDP, /* XDP is attached */
> > +       SOCK_TSTAMP_NEW, /* Indicates 64 bit timestamps always */
>
> sk_flags is getting exhausted. Commit b9f40e21ef42 ("net-timestamp:
> move timestamp flags out of sk_flags") added a new u16 sk_tsflags
> specifically for timestamps. That may be a better choice here, too.
>
> > diff --git a/net/core/sock.c b/net/core/sock.c
> > index e60036618205..7b485dfaa400 100644
> > --- a/net/core/sock.c
> > +++ b/net/core/sock.c
> > @@ -652,15 +652,23 @@ static void setsockopt_timestamp(struct sock *sk, int type, int val)
> >         if (!val) {
> >                 sock_reset_flag(sk, SOCK_RCVTSTAMP);
> >                 sock_reset_flag(sk, SOCK_RCVTSTAMPNS);
> > +               sock_reset_flag(sk, SOCK_TSTAMP_NEW);
> >                 return;
> >         }
> >
> > +       if (type == SO_TIMESTAMP_NEW || type == SO_TIMESTAMPNS_NEW)
> > +               sock_set_flag(sk, SOCK_TSTAMP_NEW);
> > +       else
> > +               sock_reset_flag(sk, SOCK_TSTAMP_NEW);
> > +
>
> if adding a boolean whether the socket uses new or old-style
> timestamps, perhaps fail hard if a process tries to set a new-style
> option while an old-style is already set and vice versa. Also include
> SO_TIMESTAMPING_NEW as it toggles the same option.
>
> > diff --git a/net/socket.c b/net/socket.c
> > index d3defba55547..9abeb6bc9cfe 100644
> > --- a/net/socket.c
> > +++ b/net/socket.c
> > @@ -699,6 +699,38 @@ static void put_ts_pktinfo(struct msghdr *msg, struct sk_buff *skb)
> >                  sizeof(ts_pktinfo), &ts_pktinfo);
> >  }
> >
> > +static void sock_recv_sw_timestamp(struct msghdr *msg, struct sock *sk,
> > +                                  struct sk_buff *skb)
> > +{
> > +       if (sock_flag(sk, SOCK_TSTAMP_NEW)) {
> > +               if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
> > +                       struct sock_timeval tv;
> > +
> > +                       skb_get_new_timestamp(skb, &tv);
> > +                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_NEW,
> > +                                sizeof(tv), &tv);
> > +               } else {
> > +                       struct __kernel_timespec ts;
> > +
> > +                       skb_get_new_timestampns(skb, &ts);
> > +                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_NEW,
> > +                                sizeof(ts), &ts);
> > +               }
> > +       }
> > +       if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
> > +               struct __kernel_old_timeval tv;
> > +
> > +               skb_get_timestamp(skb, &tv);
> > +               put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
> > +                        sizeof(tv), &tv);
> > +       } else {
> > +               struct timespec ts;
> > +
> > +               skb_get_timestampns(skb, &ts);
> > +               put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
> > +                        sizeof(ts), &ts);
> > +       }
> > +}
> >  /*
> >   * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP)
> >   * or sock_flag(sk, SOCK_RCVTSTAMPNS)
> > @@ -719,19 +751,8 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
> >                 false_tstamp = 1;
> >         }
> > -       if (need_software_tstamp) {
>
> Considerably less code churn if adding __sock_recv_timestamp_2038 and
> calling that here:
>
>                    if (sock_flag(sk, SOCK_TSTAMP_NEW))
>                            __sock_recv_timestamp_2038(msg, sk, skb);
>                    else if ...
>
> Same for the tcp case above, really, and in the case of the next patch
> for SO_TIMESTAMPING_NEW

That naming convention, ..._2038, is not the nicest, of course. That
is not the relevant bit in the above comment.

Come to think of it, and related to my question in patch 2 why the
need to rename at all, could all new structs, constants and functions
be named consistently with 64 suffix? __sock_recv_timestamp64,
SO_TIMESTAMPING64 and timeval64 (instead of sock_timeval,
it isn't really a sock specific struct)?

I guess that there is a good reason for the renaming exercise and
conditional mapping of SO_TIMESTAMP onto old or new interface.
Please elucidate in the commit message.
Deepa Dinamani Nov. 25, 2018, 5:28 a.m. UTC | #3
> > > +       if (type == SO_TIMESTAMP_NEW || type == SO_TIMESTAMPNS_NEW)
> > > +               sock_set_flag(sk, SOCK_TSTAMP_NEW);
> > > +       else
> > > +               sock_reset_flag(sk, SOCK_TSTAMP_NEW);
> > > +
> >
> > if adding a boolean whether the socket uses new or old-style
> > timestamps, perhaps fail hard if a process tries to set a new-style
> > option while an old-style is already set and vice versa. Also include
> > SO_TIMESTAMPING_NEW as it toggles the same option.

I do not think this is a problem.
Consider this example, if there is a user application with updated
socket timestamps is linking into a library that is yet to be updated.

Besides, the old timestamps should work perfectly fine on 64 bit
arches even beyond 2038.
So failing here means adding a bunch of ifdef's to verify it is not
executing on 64 bit arch or something like x32.

> > > diff --git a/net/socket.c b/net/socket.c
> > > index d3defba55547..9abeb6bc9cfe 100644
> > > --- a/net/socket.c
> > > +++ b/net/socket.c
> > > @@ -699,6 +699,38 @@ static void put_ts_pktinfo(struct msghdr *msg, struct sk_buff *skb)
> > >                  sizeof(ts_pktinfo), &ts_pktinfo);
> > >  }
> > >
> > > +static void sock_recv_sw_timestamp(struct msghdr *msg, struct sock *sk,
> > > +                                  struct sk_buff *skb)
> > > +{
> > > +       if (sock_flag(sk, SOCK_TSTAMP_NEW)) {
> > > +               if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
> > > +                       struct sock_timeval tv;
> > > +
> > > +                       skb_get_new_timestamp(skb, &tv);
> > > +                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_NEW,
> > > +                                sizeof(tv), &tv);
> > > +               } else {
> > > +                       struct __kernel_timespec ts;
> > > +
> > > +                       skb_get_new_timestampns(skb, &ts);
> > > +                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_NEW,
> > > +                                sizeof(ts), &ts);
> > > +               }
> > > +       }
> > > +       if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
> > > +               struct __kernel_old_timeval tv;
> > > +
> > > +               skb_get_timestamp(skb, &tv);
> > > +               put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
> > > +                        sizeof(tv), &tv);
> > > +       } else {
> > > +               struct timespec ts;
> > > +
> > > +               skb_get_timestampns(skb, &ts);
> > > +               put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
> > > +                        sizeof(ts), &ts);
> > > +       }
> > > +}
> > >  /*
> > >   * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP)
> > >   * or sock_flag(sk, SOCK_RCVTSTAMPNS)
> > > @@ -719,19 +751,8 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
> > >                 false_tstamp = 1;
> > >         }
> > > -       if (need_software_tstamp) {
> >
> > Considerably less code churn if adding __sock_recv_timestamp_2038 and
> > calling that here:
> >
> >                    if (sock_flag(sk, SOCK_TSTAMP_NEW))
> >                            __sock_recv_timestamp_2038(msg, sk, skb);
> >                    else if ...
> >
> > Same for the tcp case above, really, and in the case of the next patch
> > for SO_TIMESTAMPING_NEW
>
> That naming convention, ..._2038, is not the nicest, of course. That
> is not the relevant bit in the above comment.
>
> Come to think of it, and related to my question in patch 2 why the
> need to rename at all, could all new structs, constants and functions
> be named consistently with 64 suffix? __sock_recv_timestamp64,
> SO_TIMESTAMPING64 and timeval64 (instead of sock_timeval,
> it isn't really a sock specific struct)?
>
> I guess that there is a good reason for the renaming exercise and
> conditional mapping of SO_TIMESTAMP onto old or new interface.
> Please elucidate in the commit message.

I think there is some confusion here.

The existing timestamp options: SO_TIMESTAMP* fail to provide proper
timestamps beyond year 2038 on 32 bit ABIs.
But, these work fine on 64 bit native ABIs.
So now we need a way of updating these timestamps so that we do not
break existing userspace: 64 bit ABIs should not have to change
userspace, 32 bit ABIs should work as is until 2038 after which they
have bad timestamps.
So we introduce new y2038 safe timestamp options for 32 bit ABIs. We
assume that 32 bit applications will switch to new ABIs at some point,
but leave the older timestamps as is.
I can update the commit text as per above.

-Deepa
Deepa Dinamani Nov. 25, 2018, 5:55 a.m. UTC | #4
A couple of comments I missed:

> > > >  /*
> > > >   * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP)
> > > >   * or sock_flag(sk, SOCK_RCVTSTAMPNS)
> > > > @@ -719,19 +751,8 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
> > > >                 false_tstamp = 1;
> > > >         }
> > > > -       if (need_software_tstamp) {
> > >
> > > Considerably less code churn if adding __sock_recv_timestamp_2038 and
> > > calling that here:
> > >
> > >                    if (sock_flag(sk, SOCK_TSTAMP_NEW))
> > >                            __sock_recv_timestamp_2038(msg, sk, skb);
> > >                    else if ...
> > >
> > > Same for the tcp case above, really, and in the case of the next patch
> > > for SO_TIMESTAMPING_NEW
> >
> > That naming convention, ..._2038, is not the nicest, of course. That
> > is not the relevant bit in the above comment.

it could be  __sock_recv_timestamp64().
But, these timestamps should be doing exactly the same thing as the
old ones and I thought it would be nicer to keep the same code path.
I can change it to as per above.

> > Come to think of it, and related to my question in patch 2 why the
> > need to rename at all, could all new structs, constants and functions
> > be named consistently with 64 suffix? __sock_recv_timestamp64,
> > SO_TIMESTAMPING64 and timeval64 (instead of sock_timeval,
> > it isn't really a sock specific struct)?
> >
> > I guess that there is a good reason for the renaming exercise and
> > conditional mapping of SO_TIMESTAMP onto old or new interface.
> > Please elucidate in the commit message.
>
> I think there is some confusion here.
>
> The existing timestamp options: SO_TIMESTAMP* fail to provide proper
> timestamps beyond year 2038 on 32 bit ABIs.
> But, these work fine on 64 bit native ABIs.
> So now we need a way of updating these timestamps so that we do not
> break existing userspace: 64 bit ABIs should not have to change
> userspace, 32 bit ABIs should work as is until 2038 after which they
> have bad timestamps.
> So we introduce new y2038 safe timestamp options for 32 bit ABIs. We
> assume that 32 bit applications will switch to new ABIs at some point,
> but leave the older timestamps as is.
> I can update the commit text as per above.

We have been avoiding adding timeval64 timestamps to discourage users
from using these types in the interfaces.
We want to keep all the uapi time interfaces to use __kernel_*
interfaces. And, we already provide __kernel_timespec interface for
such instances.
But, in this case we do not have an option. So we introduce a type
specific to sockets.

-Deepa
Willem de Bruijn Nov. 25, 2018, 2:33 p.m. UTC | #5
On Sun, Nov 25, 2018 at 12:28 AM Deepa Dinamani <deepa.kernel@gmail.com> wrote:
>
> > > > +       if (type == SO_TIMESTAMP_NEW || type == SO_TIMESTAMPNS_NEW)
> > > > +               sock_set_flag(sk, SOCK_TSTAMP_NEW);
> > > > +       else
> > > > +               sock_reset_flag(sk, SOCK_TSTAMP_NEW);
> > > > +
> > >
> > > if adding a boolean whether the socket uses new or old-style
> > > timestamps, perhaps fail hard if a process tries to set a new-style
> > > option while an old-style is already set and vice versa. Also include
> > > SO_TIMESTAMPING_NEW as it toggles the same option.
>
> I do not think this is a problem.
> Consider this example, if there is a user application with updated
> socket timestamps is linking into a library that is yet to be updated.

Also consider applications that do not use libraries.

> Besides, the old timestamps should work perfectly fine on 64 bit
> arches even beyond 2038.

In that case, can we structure the code to not add branching on 64-bit
platforms.

For instance, structure

                  if (sock_flag(sk, SOCK_TSTAMP_NEW))
                           __sock_recv_timestamp_2038(msg, sk, skb);

instead as a boolean function that

                  if (__sock_recv_timestamp_2038(msg, sk, skb))

and have that function's contents wrapped in an ifdef that removes
it on 64-bit platforms and simply returns false?

Or more rigorously restrict these extensions to a 32-bit compat layer.

> So failing here means adding a bunch of ifdef's to verify it is not
> executing on 64 bit arch or something like x32.

The code as is adds branches on platforms that do not need it. Ifdefs
are ugly, but if they can be contained to the few helper functions needed
for the _2038 variants of cmsg_put, that is acceptable in my opinion.

> > > >  /*
> > > >   * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP)
> > > >   * or sock_flag(sk, SOCK_RCVTSTAMPNS)
> > > > @@ -719,19 +751,8 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
> > > >                 false_tstamp = 1;
> > > >         }
> > > > -       if (need_software_tstamp) {
> > >
> > > Considerably less code churn if adding __sock_recv_timestamp_2038 and
> > > calling that here:
> > >
> > >                    if (sock_flag(sk, SOCK_TSTAMP_NEW))
> > >                            __sock_recv_timestamp_2038(msg, sk, skb);
> > >                    else if ...
> > >
> > > Same for the tcp case above, really, and in the case of the next patch
> > > for SO_TIMESTAMPING_NEW
> >
> > That naming convention, ..._2038, is not the nicest, of course. That
> > is not the relevant bit in the above comment.
> >
> > Come to think of it, and related to my question in patch 2 why the
> > need to rename at all, could all new structs, constants and functions
> > be named consistently with 64 suffix? __sock_recv_timestamp64,
> > SO_TIMESTAMPING64 and timeval64 (instead of sock_timeval,
> > it isn't really a sock specific struct)?
> >
> > I guess that there is a good reason for the renaming exercise and
> > conditional mapping of SO_TIMESTAMP onto old or new interface.
> > Please elucidate in the commit message.
>
> I think there is some confusion here.

Yes, I know this socket timestamping code, but am less familiar with the
wider discusson on 2038 timestamp conversion. It would be helpful if this
patchset can be self-describing without that context or point to the
discussion (unfortunately, I had miss Arnd's talk at LPC).

> The existing timestamp options: SO_TIMESTAMP* fail to provide proper
> timestamps beyond year 2038 on 32 bit ABIs.
> But, these work fine on 64 bit native ABIs.
> So now we need a way of updating these timestamps so that we do not
> break existing userspace: 64 bit ABIs should not have to change
> userspace, 32 bit ABIs should work as is until 2038 after which they
> have bad timestamps.
> So we introduce new y2038 safe timestamp options for 32 bit ABIs. We
> assume that 32 bit applications will switch to new ABIs at some point,
> but leave the older timestamps as is.
> I can update the commit text as per above.

So on 32-bit platforms SO_TIMESTAMP_NEW introduces a new struct
sock_timeval with both 64-bit fields.

Does this not break existing applications that compile against SO_TIMESTAMP
and expect struct timeval? For one example, the selftests under tools/testing.

The kernel will now convert SO_TIMESTAMP (previously constant 29) to
different SO_TIMESTAMP_NEW (62) and returns a different struct. Perhaps
with a library like libc in the middle this can be fixed up
transparently, but for
applications that don't have a more recent libc or use a library at
all, it breaks
the ABI.

I suspect that these finer ABI points may have been discussed outside the
narrow confines of socket timestamping. But on its own, this does worry me.
Willem de Bruijn Nov. 25, 2018, 2:38 p.m. UTC | #6
> > > > Same for the tcp case above, really, and in the case of the next patch
> > > > for SO_TIMESTAMPING_NEW
> > >
> > > That naming convention, ..._2038, is not the nicest, of course. That
> > > is not the relevant bit in the above comment.
>
> it could be  __sock_recv_timestamp64().
> But, these timestamps should be doing exactly the same thing as the
> old ones and I thought it would be nicer to keep the same code path.
> I can change it to as per above.

Please minimize code changes. It breaks git blame and longer patches
are harder to review.

In this specific case, from a readability point of view, I find new functions
that map one-to-one onto the new interfaces also more readable than
deeper nested branches in place.

> > So we introduce new y2038 safe timestamp options for 32 bit ABIs. We
> > assume that 32 bit applications will switch to new ABIs at some point,
> > but leave the older timestamps as is.
> > I can update the commit text as per above.
>
> We have been avoiding adding timeval64 timestamps to discourage users
> from using these types in the interfaces.
> We want to keep all the uapi time interfaces to use __kernel_*
> interfaces. And, we already provide __kernel_timespec interface for
> such instances.
> But, in this case we do not have an option. So we introduce a type
> specific to sockets.

This structure just holds a timestamp. It does not seem socket specific.
I don't mean to bikeshed the naming point too much, but timeval_ll or
so may be more representative than tying it to a socket.

As for the general naming, xxx64 or xxx2038 are more descriptive than xxx_NEW.
Arnd Bergmann Nov. 25, 2018, 10:35 p.m. UTC | #7
On Sun, Nov 25, 2018 at 3:33 PM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
> On Sun, Nov 25, 2018 at 12:28 AM Deepa Dinamani <deepa.kernel@gmail.com> wrote:
> > So failing here means adding a bunch of ifdef's to verify it is not
> > executing on 64 bit arch or something like x32.
>
> The code as is adds branches on platforms that do not need it. Ifdefs
> are ugly, but if they can be contained to the few helper functions needed
> for the _2038 variants of cmsg_put, that is acceptable in my opinion.

In general, I think we should plan for the new code to be the fast path,
not the old one. Initially that obviously won't be the case, but I hope
that in a couple of years from now, 32-bit user space will normally use
64-bit time_t.

This also means that the compat handling for these timestamps
can be identical to the native 64-bit bit version, while the old
32-bit code can be hidden behind a flag that is only active
as the slowpath in native 32-bit mode or in compat mode.

I'd also want to see the same thing for the naming, with the
64-bit time_t based code having the obvious name.
SOCK_TSTAMP_NEW is fine, otherwise we can try to
just use SOCK_TSTAMP as the name in the kernel but
make it refer to the new version.

The two special cases we have consider are x86 with x32
user space, which wants 64-bit timestamps in compat mode
with SOCK_TSTAMP_OLD, and sparc64, which has an
incompatible layout of timeval, so we need to make sure
that sparc64 can handle all three layouts correctly.

> > The existing timestamp options: SO_TIMESTAMP* fail to provide proper
> > timestamps beyond year 2038 on 32 bit ABIs.
> > But, these work fine on 64 bit native ABIs.
> > So now we need a way of updating these timestamps so that we do not
> > break existing userspace: 64 bit ABIs should not have to change
> > userspace, 32 bit ABIs should work as is until 2038 after which they
> > have bad timestamps.
> > So we introduce new y2038 safe timestamp options for 32 bit ABIs. We
> > assume that 32 bit applications will switch to new ABIs at some point,
> > but leave the older timestamps as is.
> > I can update the commit text as per above.
>
> So on 32-bit platforms SO_TIMESTAMP_NEW introduces a new struct
> sock_timeval with both 64-bit fields.
>
> Does this not break existing applications that compile against SO_TIMESTAMP
> and expect struct timeval? For one example, the selftests under tools/testing.
>
> The kernel will now convert SO_TIMESTAMP (previously constant 29) to
> different SO_TIMESTAMP_NEW (62) and returns a different struct. Perhaps
> with a library like libc in the middle this can be fixed up
> transparently, but for
> applications that don't have a more recent libc or use a library at
> all, it breaks
> the ABI.
>
> I suspect that these finer ABI points may have been discussed outside the
> narrow confines of socket timestamping. But on its own, this does worry me.

The entire purpose of the complexities in the patch set is to not break
the user space ABI after an application gets recompiled with a 64-bit
time_t defined by a new libc version:

#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? \
            SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)

This delays the evaluation of SO_TIMESTAMP to the point where
it is first used, the assumption being that at this point we have included
the libc header file that defines both 'time_t' and 'struct timeval'.
[If we have not included that header, we get a compile-time error,
which is also necessary because the compiler has no way of deciding
whether to use SO_TIMESTAMP_OLD or SO_TIMESTAMP_NEW
in that case].

If the application is built with a 32-bit time_t, or with on a 64-bit
architecture (all of which have 64-bit time_t and __kernel_long_t),
or on x32 (which also has 64-bit time_t and __kernel_long_t),
the result is SO_TIMESTAMP_OLD, so we tell the kernel
to send back a timestamp in the old format, and everything works
as it did before.

The only thing that changes is 32-bit user space (other than x32) with
a 64-bit time_t. In this case, the application expects a structure
that corresponds to the new sock_timeval (which is compatible
with the user space timeval based on 64-bit time_t), so we must
use the new constant in order to tell the kernel which one we want.

      Arnd
Willem de Bruijn Nov. 26, 2018, 12:25 a.m. UTC | #8
> > > The existing timestamp options: SO_TIMESTAMP* fail to provide proper
> > > timestamps beyond year 2038 on 32 bit ABIs.
> > > But, these work fine on 64 bit native ABIs.
> > > So now we need a way of updating these timestamps so that we do not
> > > break existing userspace: 64 bit ABIs should not have to change
> > > userspace, 32 bit ABIs should work as is until 2038 after which they
> > > have bad timestamps.
> > > So we introduce new y2038 safe timestamp options for 32 bit ABIs. We
> > > assume that 32 bit applications will switch to new ABIs at some point,
> > > but leave the older timestamps as is.
> > > I can update the commit text as per above.
> >
> > So on 32-bit platforms SO_TIMESTAMP_NEW introduces a new struct
> > sock_timeval with both 64-bit fields.
> >
> > Does this not break existing applications that compile against SO_TIMESTAMP
> > and expect struct timeval? For one example, the selftests under tools/testing.
> >
> > The kernel will now convert SO_TIMESTAMP (previously constant 29) to
> > different SO_TIMESTAMP_NEW (62) and returns a different struct. Perhaps
> > with a library like libc in the middle this can be fixed up
> > transparently, but for
> > applications that don't have a more recent libc or use a library at
> > all, it breaks
> > the ABI.
> >
> > I suspect that these finer ABI points may have been discussed outside the
> > narrow confines of socket timestamping. But on its own, this does worry me.
>
> The entire purpose of the complexities in the patch set is to not break
> the user space ABI after an application gets recompiled with a 64-bit
> time_t defined by a new libc version:
>
> #define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? \
>             SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
>
> This delays the evaluation of SO_TIMESTAMP to the point where
> it is first used, the assumption being that at this point we have included
> the libc header file that defines both 'time_t' and 'struct timeval'.
> [If we have not included that header, we get a compile-time error,
> which is also necessary because the compiler has no way of deciding
> whether to use SO_TIMESTAMP_OLD or SO_TIMESTAMP_NEW
> in that case].
>
> If the application is built with a 32-bit time_t, or with on a 64-bit
> architecture (all of which have 64-bit time_t and __kernel_long_t),
> or on x32 (which also has 64-bit time_t and __kernel_long_t),
> the result is SO_TIMESTAMP_OLD, so we tell the kernel
> to send back a timestamp in the old format, and everything works
> as it did before.
>
> The only thing that changes is 32-bit user space (other than x32) with
> a 64-bit time_t. In this case, the application expects a structure
> that corresponds to the new sock_timeval (which is compatible
> with the user space timeval based on 64-bit time_t), so we must
> use the new constant in order to tell the kernel which one we want.

Thanks. That was exactly the context that I was missing. I hadn't
figured out that the test was based on a libc definition. This all
makes perfect sense.
Deepa Dinamani Nov. 30, 2018, 10:43 p.m. UTC | #9
On Sun, Nov 25, 2018 at 6:33 AM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> On Sun, Nov 25, 2018 at 12:28 AM Deepa Dinamani <deepa.kernel@gmail.com> wrote:
> >
> > > > > +       if (type == SO_TIMESTAMP_NEW || type == SO_TIMESTAMPNS_NEW)
> > > > > +               sock_set_flag(sk, SOCK_TSTAMP_NEW);
> > > > > +       else
> > > > > +               sock_reset_flag(sk, SOCK_TSTAMP_NEW);
> > > > > +
> > > >
> > > > if adding a boolean whether the socket uses new or old-style
> > > > timestamps, perhaps fail hard if a process tries to set a new-style
> > > > option while an old-style is already set and vice versa. Also include
> > > > SO_TIMESTAMPING_NEW as it toggles the same option.
> >
> > I do not think this is a problem.
> > Consider this example, if there is a user application with updated
> > socket timestamps is linking into a library that is yet to be updated.
>
> Also consider applications that do not use libraries.

Arnd and I talked about this.
We thought that the new options should behave like the already
existing options. The patch already does this.
Eg: Today if we set SO_TIMESTAMP and then try to switch to
SO_TIMESTAMPNS then there is no fail.

Do you still want a hard fail?

-Deepa
Willem de Bruijn Nov. 30, 2018, 11:37 p.m. UTC | #10
On Fri, Nov 30, 2018 at 5:43 PM Deepa Dinamani <deepa.kernel@gmail.com> wrote:
>
> On Sun, Nov 25, 2018 at 6:33 AM Willem de Bruijn
> <willemdebruijn.kernel@gmail.com> wrote:
> >
> > On Sun, Nov 25, 2018 at 12:28 AM Deepa Dinamani <deepa.kernel@gmail.com> wrote:
> > >
> > > > > > +       if (type == SO_TIMESTAMP_NEW || type == SO_TIMESTAMPNS_NEW)
> > > > > > +               sock_set_flag(sk, SOCK_TSTAMP_NEW);
> > > > > > +       else
> > > > > > +               sock_reset_flag(sk, SOCK_TSTAMP_NEW);
> > > > > > +
> > > > >
> > > > > if adding a boolean whether the socket uses new or old-style
> > > > > timestamps, perhaps fail hard if a process tries to set a new-style
> > > > > option while an old-style is already set and vice versa. Also include
> > > > > SO_TIMESTAMPING_NEW as it toggles the same option.
> > >
> > > I do not think this is a problem.
> > > Consider this example, if there is a user application with updated
> > > socket timestamps is linking into a library that is yet to be updated.
> >
> > Also consider applications that do not use libraries.
>
> Arnd and I talked about this.
> We thought that the new options should behave like the already
> existing options. The patch already does this.
> Eg: Today if we set SO_TIMESTAMP and then try to switch to
> SO_TIMESTAMPNS then there is no fail.

> Do you still want a hard fail?

I do think that it is preferable. In general, and in this specific case.

We have had had many bug reports from syzkaller where the fuzzer
manages to trigger unexpected behavior by combining two APIs
that were never intended to be used together.

However inane the combination may be, once an API is published,
we cannot simply add an EINVAL and stop supporting it. So it is safer
to explicitly block unsafe combinations from the start. If there is a
legitimate use it is always possible to loosen that restriction later.

I don't see any sensible use for mixing both the old and the new
interface on the same socket.

That said, just a suggestion.
diff mbox series

Patch

diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h
index 00e45c80e574..352e3dc0b3d9 100644
--- a/arch/alpha/include/uapi/asm/socket.h
+++ b/arch/alpha/include/uapi/asm/socket.h
@@ -3,6 +3,7 @@ 
 #define _UAPI_ASM_SOCKET_H
 
 #include <asm/sockios.h>
+#include <asm/bitsperlong.h>
 
 /* For setsockopt(2) */
 /*
@@ -110,12 +111,22 @@ 
 
 #define SO_TIMESTAMP_OLD         29
 #define SO_TIMESTAMPNS_OLD       35
+
 #define SO_TIMESTAMPING_OLD      37
 
+#define SO_TIMESTAMP_NEW         62
+#define SO_TIMESTAMPNS_NEW       63
+
 #if !defined(__KERNEL__)
 
-#define SO_TIMESTAMP           SO_TIMESTAMP_OLD
-#define SO_TIMESTAMPNS         SO_TIMESTAMPNS_OLD
+#if __BITS_PER_LONG == 64
+#define SO_TIMESTAMP		SO_TIMESTAMP_OLD
+#define SO_TIMESTAMPNS		SO_TIMESTAMPNS_OLD
+#else
+#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
+#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW)
+#endif
+
 #define SO_TIMESTAMPING        SO_TIMESTAMPING_OLD
 
 #define SCM_TIMESTAMP          SO_TIMESTAMP
diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h
index b9553f770346..d1752e3f1248 100644
--- a/arch/mips/include/uapi/asm/socket.h
+++ b/arch/mips/include/uapi/asm/socket.h
@@ -11,6 +11,7 @@ 
 #define _UAPI_ASM_SOCKET_H
 
 #include <asm/sockios.h>
+#include <asm/bitsperlong.h>
 
 /*
  * For setsockopt(2)
@@ -123,10 +124,19 @@ 
 #define SO_TIMESTAMPNS_OLD       35
 #define SO_TIMESTAMPING_OLD      37
 
+#define SO_TIMESTAMP_NEW         62
+#define SO_TIMESTAMPNS_NEW       63
+
 #if !defined(__KERNEL__)
 
-#define SO_TIMESTAMP           SO_TIMESTAMP_OLD
-#define SO_TIMESTAMPNS         SO_TIMESTAMPNS_OLD
+#if __BITS_PER_LONG == 64
+#define SO_TIMESTAMP		SO_TIMESTAMP_OLD
+#define SO_TIMESTAMPNS		SO_TIMESTAMPNS_OLD
+#else
+#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
+#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW)
+#endif
+
 #define SO_TIMESTAMPING        SO_TIMESTAMPING_OLD
 
 #define SCM_TIMESTAMP          SO_TIMESTAMP
diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h
index 37cdfe64bb27..0a45b668abd1 100644
--- a/arch/parisc/include/uapi/asm/socket.h
+++ b/arch/parisc/include/uapi/asm/socket.h
@@ -3,6 +3,7 @@ 
 #define _UAPI_ASM_SOCKET_H
 
 #include <asm/sockios.h>
+#include <asm/bitsperlong.h>
 
 /* For setsockopt(2) */
 #define SOL_SOCKET	0xffff
@@ -104,10 +105,19 @@ 
 #define SO_TIMESTAMPNS_OLD       0x4013
 #define SO_TIMESTAMPING_OLD      0x4020
 
+#define SO_TIMESTAMP_NEW         0x4037
+#define SO_TIMESTAMPNS_NEW       0x4038
+
 #if !defined(__KERNEL__)
 
-#define SO_TIMESTAMP           SO_TIMESTAMP_OLD
-#define SO_TIMESTAMPNS         SO_TIMESTAMPNS_OLD
+#if __BITS_PER_LONG == 64
+#define SO_TIMESTAMP		SO_TIMESTAMP_OLD
+#define SO_TIMESTAMPNS		SO_TIMESTAMPNS_OLD
+#else
+#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
+#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW)
+#endif
+
 #define SO_TIMESTAMPING        SO_TIMESTAMPING_OLD
 
 #define SCM_TIMESTAMP          SO_TIMESTAMP
diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h
index ca573641fc6c..dc8527cae5a7 100644
--- a/arch/sparc/include/uapi/asm/socket.h
+++ b/arch/sparc/include/uapi/asm/socket.h
@@ -3,6 +3,7 @@ 
 #define _ASM_SOCKET_H
 
 #include <asm/sockios.h>
+#include <asm/bitsperlong.h>
 
 /* For setsockopt(2) */
 #define SOL_SOCKET	0xffff
@@ -105,10 +106,19 @@ 
 #define SO_TIMESTAMPNS_OLD       0x0021
 #define SO_TIMESTAMPING_OLD      0x0023
 
+#define SO_TIMESTAMP_NEW         0x0040
+#define SO_TIMESTAMPNS_NEW       0x0041
+
 #if !defined(__KERNEL__)
 
-#define SO_TIMESTAMP           SO_TIMESTAMP_OLD
-#define SO_TIMESTAMPNS         SO_TIMESTAMPNS_OLD
+#if __BITS_PER_LONG == 64
+#define SO_TIMESTAMP		SO_TIMESTAMP_OLD
+#define SO_TIMESTAMPNS		SO_TIMESTAMPNS_OLD
+#else
+#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
+#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW)
+#endif
+
 #define SO_TIMESTAMPING        SO_TIMESTAMPING_OLD
 
 #define SCM_TIMESTAMP          SO_TIMESTAMP
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index e2dc01330cb1..1e42c4a2209d 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -3465,12 +3465,30 @@  static inline void skb_get_timestamp(const struct sk_buff *skb,
 	*stamp = ns_to_kernel_old_timeval(skb->tstamp);
 }
 
+static inline void skb_get_new_timestamp(const struct sk_buff *skb,
+				     struct sock_timeval *stamp)
+{
+	struct timespec64 ts = ktime_to_timespec64(skb->tstamp);
+
+	stamp->tv_sec = ts.tv_sec;
+	stamp->tv_usec = ts.tv_nsec / 1000;
+}
+
 static inline void skb_get_timestampns(const struct sk_buff *skb,
 				       struct timespec *stamp)
 {
 	*stamp = ktime_to_timespec(skb->tstamp);
 }
 
+static inline void skb_get_new_timestampns(const struct sk_buff *skb,
+				       struct __kernel_timespec *stamp)
+{
+	struct timespec64 ts = ktime_to_timespec64(skb->tstamp);
+
+	stamp->tv_sec = ts.tv_sec;
+	stamp->tv_nsec = ts.tv_nsec;
+}
+
 static inline void __net_timestamp(struct sk_buff *skb)
 {
 	skb->tstamp = ktime_get_real();
diff --git a/include/net/sock.h b/include/net/sock.h
index 8143c4c1a49d..9edf909dc176 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -801,6 +801,7 @@  enum sock_flags {
 	SOCK_RCU_FREE, /* wait rcu grace period in sk_destruct() */
 	SOCK_TXTIME,
 	SOCK_XDP, /* XDP is attached */
+	SOCK_TSTAMP_NEW, /* Indicates 64 bit timestamps always */
 };
 
 #define SK_FLAGS_TIMESTAMP ((1UL << SOCK_TIMESTAMP) | (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE))
diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
index dc704e41203d..0b0fae6b57a9 100644
--- a/include/uapi/asm-generic/socket.h
+++ b/include/uapi/asm-generic/socket.h
@@ -3,6 +3,7 @@ 
 #define __ASM_GENERIC_SOCKET_H
 
 #include <asm/sockios.h>
+#include <asm/bitsperlong.h>
 
 /* For setsockopt(2) */
 #define SOL_SOCKET	1
@@ -107,10 +108,20 @@ 
 #define SO_TIMESTAMPNS_OLD       35
 #define SO_TIMESTAMPING_OLD      37
 
+#define SO_TIMESTAMP_NEW         62
+#define SO_TIMESTAMPNS_NEW       63
+
 #if !defined(__KERNEL__)
 
-#define SO_TIMESTAMP           SO_TIMESTAMP_OLD
-#define SO_TIMESTAMPNS         SO_TIMESTAMPNS_OLD
+#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
+/* on 64-bit and x32, avoid the ?: operator */
+#define SO_TIMESTAMP		SO_TIMESTAMP_OLD
+#define SO_TIMESTAMPNS		SO_TIMESTAMPNS_OLD
+#else
+#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
+#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW)
+#endif
+
 #define SO_TIMESTAMPING        SO_TIMESTAMPING_OLD
 
 #define SCM_TIMESTAMP          SO_TIMESTAMP
diff --git a/net/core/sock.c b/net/core/sock.c
index e60036618205..7b485dfaa400 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -652,15 +652,23 @@  static void setsockopt_timestamp(struct sock *sk, int type, int val)
 	if (!val) {
 		sock_reset_flag(sk, SOCK_RCVTSTAMP);
 		sock_reset_flag(sk, SOCK_RCVTSTAMPNS);
+		sock_reset_flag(sk, SOCK_TSTAMP_NEW);
 		return;
 	}
 
+	if (type == SO_TIMESTAMP_NEW || type == SO_TIMESTAMPNS_NEW)
+		sock_set_flag(sk, SOCK_TSTAMP_NEW);
+	else
+		sock_reset_flag(sk, SOCK_TSTAMP_NEW);
+
 	switch (type) {
 	case SO_TIMESTAMP_OLD:
+	case SO_TIMESTAMP_NEW:
 		sock_reset_flag(sk, SOCK_RCVTSTAMPNS);
 		sock_set_flag(sk, SOCK_RCVTSTAMP);
 		break;
 	case SO_TIMESTAMPNS_OLD:
+	case SO_TIMESTAMPNS_NEW:
 		sock_reset_flag(sk, SOCK_RCVTSTAMP);
 		sock_set_flag(sk, SOCK_RCVTSTAMPNS);
 		break;
@@ -837,7 +845,9 @@  int sock_setsockopt(struct socket *sock, int level, int optname,
 		break;
 
 	case SO_TIMESTAMP_OLD:
+	case SO_TIMESTAMP_NEW:
 	case SO_TIMESTAMPNS_OLD:
+	case SO_TIMESTAMPNS_NEW:
 		setsockopt_timestamp(sk, optname, valbool);
 		break;
 
@@ -1202,6 +1212,14 @@  int sock_getsockopt(struct socket *sock, int level, int optname,
 		v.val = sock_flag(sk, SOCK_RCVTSTAMPNS);
 		break;
 
+	case SO_TIMESTAMP_NEW:
+		v.val = sock_flag(sk, SOCK_RCVTSTAMP) && sock_flag(sk, SOCK_TSTAMP_NEW);
+		break;
+
+	case SO_TIMESTAMPNS_NEW:
+		v.val = sock_flag(sk, SOCK_RCVTSTAMPNS) && sock_flag(sk, SOCK_TSTAMP_NEW);
+		break;
+
 	case SO_TIMESTAMPING_OLD:
 		v.val = sk->sk_tsflags;
 		break;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 805d9965a210..b470f470343a 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1860,30 +1860,57 @@  static void tcp_update_recv_tstamps(struct sk_buff *skb,
 		tss->ts[2] = (struct timespec) {0};
 }
 
-/* Similar to __sock_recv_timestamp, but does not require an skb */
-static void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk,
-			       struct scm_timestamping *tss)
+static void tcp_recv_sw_timestamp(struct msghdr *msg, const struct sock *sk, struct timespec64 *ts)
 {
-	struct __kernel_old_timeval tv;
-	bool has_timestamping = false;
+	int new_tstamp = sock_flag(sk, SOCK_TSTAMP_NEW);
 
-	if (tss->ts[0].tv_sec || tss->ts[0].tv_nsec) {
+	if (ts->tv_sec || ts->tv_nsec) {
 		if (sock_flag(sk, SOCK_RCVTSTAMPNS)) {
-			put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
-				 sizeof(tss->ts[0]), &tss->ts[0]);
+			if (new_tstamp) {
+				struct __kernel_timespec kts = {ts->tv_sec, ts->tv_nsec};
+
+				put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_NEW,
+					 sizeof(kts), &kts);
+			} else {
+				struct timespec ts_old = timespec64_to_timespec(*ts);
+
+				put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
+					 sizeof(ts), &ts_old);
+			}
 		} else if (sock_flag(sk, SOCK_RCVTSTAMP)) {
-			tv.tv_sec = tss->ts[0].tv_sec;
-			tv.tv_usec = tss->ts[0].tv_nsec / 1000;
+			if (new_tstamp) {
+				struct sock_timeval stv;
 
-			put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
-				 sizeof(tv), &tv);
-		}
+				stv.tv_sec = ts->tv_sec;
+				stv.tv_usec = ts->tv_nsec / 1000;
 
-		if (sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE)
-			has_timestamping = true;
-		else
-			tss->ts[0] = (struct timespec) {0};
+				put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_NEW,
+					 sizeof(stv), &stv);
+			} else {
+				struct __kernel_old_timeval tv;
+
+				tv.tv_sec = ts->tv_sec;
+				tv.tv_usec = ts->tv_nsec / 1000;
+				put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_NEW,
+					 sizeof(tv), &tv);
+			}
+		}
 	}
+}
+
+/* Similar to __sock_recv_timestamp, but does not require an skb */
+static void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk,
+			       struct scm_timestamping *tss)
+{
+	bool has_timestamping = false;
+	struct timespec64 ts = timespec_to_timespec64(tss->ts[0]);
+
+	tcp_recv_sw_timestamp(msg, sk, &ts);
+
+	if (sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE)
+		has_timestamping = true;
+	else
+		tss->ts[0] = (struct timespec) {0};
 
 	if (tss->ts[2].tv_sec || tss->ts[2].tv_nsec) {
 		if (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE)
diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c
index eeb4639adbe5..65571a6273c3 100644
--- a/net/rds/af_rds.c
+++ b/net/rds/af_rds.c
@@ -348,7 +348,7 @@  static int rds_set_transport(struct rds_sock *rs, char __user *optval,
 }
 
 static int rds_enable_recvtstamp(struct sock *sk, char __user *optval,
-				 int optlen)
+				 int optlen, int optname)
 {
 	int val, valbool;
 
@@ -360,6 +360,9 @@  static int rds_enable_recvtstamp(struct sock *sk, char __user *optval,
 
 	valbool = val ? 1 : 0;
 
+	if (optname == SO_TIMESTAMP_NEW)
+		sock_set_flag(sk, SOCK_TSTAMP_NEW);
+
 	if (valbool)
 		sock_set_flag(sk, SOCK_RCVTSTAMP);
 	else
@@ -431,8 +434,9 @@  static int rds_setsockopt(struct socket *sock, int level, int optname,
 		release_sock(sock->sk);
 		break;
 	case SO_TIMESTAMP_OLD:
+	case SO_TIMESTAMP_NEW:
 		lock_sock(sock->sk);
-		ret = rds_enable_recvtstamp(sock->sk, optval, optlen);
+		ret = rds_enable_recvtstamp(sock->sk, optval, optlen, optname);
 		release_sock(sock->sk);
 		break;
 	case SO_RDS_MSG_RXPATH_LATENCY:
diff --git a/net/rds/recv.c b/net/rds/recv.c
index 435bf2320cd3..4c3fd56dc4ca 100644
--- a/net/rds/recv.c
+++ b/net/rds/recv.c
@@ -550,8 +550,20 @@  static int rds_cmsg_recv(struct rds_incoming *inc, struct msghdr *msg,
 	if ((inc->i_rx_tstamp != 0) &&
 	    sock_flag(rds_rs_to_sk(rs), SOCK_RCVTSTAMP)) {
 		struct __kernel_old_timeval tv = ns_to_kernel_old_timeval(inc->i_rx_tstamp);
-		ret = put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
-			       sizeof(tv), &tv);
+
+		if (!sock_flag(rds_rs_to_sk(rs), SOCK_TSTAMP_NEW)) {
+			ret = put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
+				       sizeof(tv), &tv);
+		} else {
+			struct sock_timeval sk_tv;
+
+			sk_tv.tv_sec = tv.tv_sec;
+			sk_tv.tv_usec = tv.tv_usec;
+
+			ret = put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_NEW,
+				       sizeof(sk_tv), &sk_tv);
+		}
+
 		if (ret)
 			goto out;
 	}
diff --git a/net/socket.c b/net/socket.c
index d3defba55547..9abeb6bc9cfe 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -699,6 +699,38 @@  static void put_ts_pktinfo(struct msghdr *msg, struct sk_buff *skb)
 		 sizeof(ts_pktinfo), &ts_pktinfo);
 }
 
+static void sock_recv_sw_timestamp(struct msghdr *msg, struct sock *sk,
+				   struct sk_buff *skb)
+{
+	if (sock_flag(sk, SOCK_TSTAMP_NEW)) {
+		if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
+			struct sock_timeval tv;
+
+			skb_get_new_timestamp(skb, &tv);
+			put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_NEW,
+				 sizeof(tv), &tv);
+		} else {
+			struct __kernel_timespec ts;
+
+			skb_get_new_timestampns(skb, &ts);
+			put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_NEW,
+				 sizeof(ts), &ts);
+		}
+	}
+	if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
+		struct __kernel_old_timeval tv;
+
+		skb_get_timestamp(skb, &tv);
+		put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
+			 sizeof(tv), &tv);
+	} else {
+		struct timespec ts;
+
+		skb_get_timestampns(skb, &ts);
+		put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
+			 sizeof(ts), &ts);
+	}
+}
 /*
  * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP)
  * or sock_flag(sk, SOCK_RCVTSTAMPNS)
@@ -719,19 +751,8 @@  void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
 		false_tstamp = 1;
 	}
 
-	if (need_software_tstamp) {
-		if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
-			struct __kernel_old_timeval tv;
-			skb_get_timestamp(skb, &tv);
-			put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
-				 sizeof(tv), &tv);
-		} else {
-			struct timespec ts;
-			skb_get_timestampns(skb, &ts);
-			put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
-				 sizeof(ts), &ts);
-		}
-	}
+	if (need_software_tstamp)
+		sock_recv_sw_timestamp(msg, sk, skb);
 
 	memset(&tss, 0, sizeof(tss));
 	if ((sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) &&