[v9,0/2] Support semctl_syscall() for __TIMESIZE==64

Message ID 20200602204105.43866-1-alistair.francis@wdc.com
Headers
Series Support semctl_syscall() for __TIMESIZE==64 |

Message

Alistair Francis June 2, 2020, 8:41 p.m. UTC
  This series supports the semctl calls on a system with __TIMESIZE==64
and __WORDSIZE==32 while not breaking current architectures. This is a
step towards full y2038 support, but does not get us there yet.

See: https://sourceware.org/pipermail/libc-alpha/2020-May/113774.html
for more details on what is still required.

This series adds a new __semid_ds32 that is passed to the kernel
(as part of a union) when running on 32-bit systems. If we are doing an
IPC_STAT/SEM_STAT command then the 32-bit sem_{c,o}time{_high} values
are combined to create a 64-bit value.

The semctl_syscall() function passes a union semun to the kernel. The
union includes struct semid_ds as a member. On 32-bit architectures the
Linux kernel provides a *_high version of the 32-bit sem_otime and
sem_ctime values. These can be combined to get a 64-bit version of the
time.

This patch adjusts the struct semid_ds to support the *_high versions
of sem_otime and sem_ctime. For 32-bit systems with a 64-bit time_t
this can be used to get a 64-bit time from the two 32-bit values.

This series was tested by running:
  ./scripts/build-many-glibcs.py ... compilers
  ./scripts/build-many-glibcs.py ... glibcs
on my x86_64 machine.

I also ran make check on RV32 and I only see a total of 9 test failures.

v9:
 - Don't use defined inside a #define
v8:
 - Revert back to v6-ish and only support __TIMESIZE==64
v7:
 - Re-write based on code from Adhemerval.
v6:
 - Update the 3rd patch to pass a temp buffer to the kernel
v5:
 - Address v4 review comments
 - Set the semid_ds struct from a temp struct
v4:
 - Remove the __IPC_TIME64 macro
    - It was only used once and doesn't work if __IPC_64 is 0 (which is
      usually is)
 - Address failures pointed out by Vineet Gupta

Alistair Francis (2):
  sysv: linux: Define the __semid_ds32 struct
  sysv: linux: Pass 64-bit version of semctl syscall

 .../unix/sysv/linux/hppa/struct__semid_ds32.h | 30 +++++++++++
 sysdeps/unix/sysv/linux/ipc_priv.h            |  7 +++
 .../unix/sysv/linux/mips/struct__semid_ds32.h | 28 +++++++++++
 .../sysv/linux/powerpc/struct__semid_ds32.h   | 30 +++++++++++
 sysdeps/unix/sysv/linux/semctl.c              | 50 +++++++++++++++++--
 .../sysv/linux/sparc/struct__semid_ds32.h     | 30 +++++++++++
 sysdeps/unix/sysv/linux/struct__semid_ds32.h  | 30 +++++++++++
 .../unix/sysv/linux/x86/struct__semid_ds32.h  | 30 +++++++++++
 8 files changed, 231 insertions(+), 4 deletions(-)
 create mode 100644 sysdeps/unix/sysv/linux/hppa/struct__semid_ds32.h
 create mode 100644 sysdeps/unix/sysv/linux/mips/struct__semid_ds32.h
 create mode 100644 sysdeps/unix/sysv/linux/powerpc/struct__semid_ds32.h
 create mode 100644 sysdeps/unix/sysv/linux/sparc/struct__semid_ds32.h
 create mode 100644 sysdeps/unix/sysv/linux/struct__semid_ds32.h
 create mode 100644 sysdeps/unix/sysv/linux/x86/struct__semid_ds32.h
  

Comments

Adhemerval Zanella Netto June 29, 2020, 8:51 p.m. UTC | #1
On 02/06/2020 17:41, Alistair Francis wrote:
> This series supports the semctl calls on a system with __TIMESIZE==64
> and __WORDSIZE==32 while not breaking current architectures. This is a
> step towards full y2038 support, but does not get us there yet.
> 
> See: https://sourceware.org/pipermail/libc-alpha/2020-May/113774.html
> for more details on what is still required.
> 
> This series adds a new __semid_ds32 that is passed to the kernel
> (as part of a union) when running on 32-bit systems. If we are doing an
> IPC_STAT/SEM_STAT command then the 32-bit sem_{c,o}time{_high} values
> are combined to create a 64-bit value.

I think this patch still does not go in to the direction it was discussed
in last iteration. This patch just handles the case of 32-bit
architectures with only 64-bit time_t support, where to fully cover all 
possible scenarios we will need to handle the 32-bit architectures where
it would be possible to select the time_t ABI (i386 for instance).

To move this support forward I implemented the idea we discussed in a
personal branch [1].  Instead of adding the the '__semid_ds32' I added
two new structures:

  1. kernel_semid64_ds: used internally only on 32-bit architectures
     to issue the syscall.  As for __semid_ds32, a handful architectures
     (hppa, i386, mips, powerpc32, sparc32) requires specific 
     implementation due its specific kernel ABI.

  2. semid_ds64: this is only for __TIMESIZE != 64 to used along with
     the 64-bit semctl.  It is different than the kernel one because
     the exported 64-bit time_t might require different alignment
     depending of the architecture ABI.  

So the resulting implementation does:


  1. For 64-bit architectures it assumes semid_ds already contains
     64-bit time_t fields it will result in just the __semctl symbol
     using the __semctl64 code:

     static int
     semctl_syscall (int semid, int semnum, int cmd, union semun arg)
     {
       [...]
     }

     int
     semctl ([...])
     {
       union semun arg64 = { 0 };
       [...]
       switch (cmd)
         {
         [...]
           arg64 = va_arg (ap, union semun);
         [...]
         }

       union semun arg = arg64;
 
       int ret = semctl_syscall (semid, semnum, cmd, arg);
       [...]
     }

     Basically the semid_ds argument is passed as-is to the kernel
     interface.


  2. For 32-bit architectures with default 64-bit time_t (newer ABIs
     such riscv32 or arc), it will also result in only one symbol
     but with the required high/low handling:

     static int
     semctl_syscall (int semid, int semnum, int cmd, union ksemun64 arg)
     {
       [...]
     }

     int
     semctl ([...])
     {
       union semun arg64 = { 0 };
       [...]
       switch (cmd)
         {
         [...]
           arg64 = va_arg (ap, union semun64);
         [...]
         }

       struct kernel_semid64_ds ksemid;
       union ksemun64 ksemun = semun64_to_ksemun64 (cmd, arg64, &ksemid);
       union ksemun64 arg = ksemun;

       int ret = semctl_syscall (semid, semnum, cmd, arg);
       [...]

       switch (cmd)
         {
         case IPC_STAT:
         case SEM_STAT:
         case SEM_STAT_ANY:
         [...]
         ksemid64_to_semid64 (arg.buf, arg64.buf);
         }
       [...]
     }

     It might be possible to optimize it further to avoid the 
     kernel_semid64_ds to semun transformation if the exported glibc ABI
     for the architectures matches the expected kernel ABI, but the 
     implementation is already complex enough and don't think this should
     be a hotspot in any case.
    

  3. Finally for 32-bit architecture with both 32-bit and 64-bit time_t
     support we follow the already set way to provide one symbol with
     64-bit time_t support and implement the 32-bit time_t support on
     basis of the 64-bit one:

     static int
     semctl_syscall (int semid, int semnum, int cmd, union ksemun64 arg)
     {
       [...]
     }

     int
     __semctl64 ([...])
     {
       union semun64 arg64 = { 0 };
       [...]
       switch (cmd)
         {
           [...]
           arg64 = va_arg (ap, union semun64);
           [...]
         }

       struct kernel_semid64_ds ksemid;
       union ksemun64 ksemun = semun64_to_ksemun64 (cmd, arg64, &ksemid);
       union ksemun64 arg = ksemun;

       int ret = semctl_syscall (semid, semnum, cmd, arg);
       [...]

       switch (cmd)
         {
         case IPC_STAT:
         case SEM_STAT:
         case SEM_STAT_ANY:
         [...]
         ksemid64_to_semid64 (arg.buf, arg64.buf);
         }
       [...]
     }

     int
     __semctl (int semid, int semnum, int cmd, ...)
     {
       union semun arg = { 0 };
       [...]
       switch (cmd)
         {
         [...]
           arg = va_arg (ap, union semun);
         [...]
         }

       struct __semid64_ds semid64;
       union semun64 arg64 = semun_to_semun64 (cmd, arg, &semid64);

       int ret = __semctl64 (semid, semnum, cmd, arg64);

       switch (cmd)
         {
         case IPC_STAT:
         case SEM_STAT:
         case SEM_STAT_ANY:
           semid64_ds_to_semid_ds (arg.buf, arg64.buf);
         }
     }

     The default 32-bit symbol will allocate and copy the semid_ds
     over multiple buffers, but this should be deprecated in favor
     of the __semctl64 anyway.


I did some sniff tests on i686, arm, mips, and powerpc where this 
code should change the way the function is implemented.  Could you
also check on riscv32 since currently there is no 32-bit architecture
with default 64-bit time_t? If it ok for riscv32 I can send it 
upstream.


[1] https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/azanella/semctl-y2038