bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH gnumach] x86_64: avoid iterating over the message twice in co


From: Samuel Thibault
Subject: Re: [PATCH gnumach] x86_64: avoid iterating over the message twice in copyoutmsg/copyinmsg for faster RPCs.
Date: Mon, 11 Mar 2024 23:49:14 +0100
User-agent: NeoMutt/20170609 (1.8.3)

Applied, thanks!

Flavio Cruz, le lun. 19 févr. 2024 23:58:00 -0500, a ecrit:
> This is a follow up to
> https://git.savannah.gnu.org/cgit/hurd/gnumach.git/commit/?id=69620634858b2992e1a362e33c95d9a8ee57bce7
> where we made inlined ports 8 bytes long to avoid resizing.
> 
> The last thing that copy{in,out}msg were doing was just updating
> msgt_size field since that's required for kernel stub code and implicitly
> assumed by IPC code. This was moved into ipc_kmsg_copy{in,out}_body.
> 
> For a 32 bit userland, the code also stops updating
> msgt_size for out of line ports, same as the 64 bit userland.
> ---
>  i386/i386/locore.h |   6 +++
>  ipc/ipc_kmsg.c     |  52 +++++++++++++------
>  x86_64/copy_user.c | 123 +++++++++------------------------------------
>  3 files changed, 66 insertions(+), 115 deletions(-)
> 
> diff --git a/i386/i386/locore.h b/i386/i386/locore.h
> index 374c8cf..217e6de 100644
> --- a/i386/i386/locore.h
> +++ b/i386/i386/locore.h
> @@ -50,7 +50,13 @@ extern int discover_x86_cpu_type (void);
>  extern int copyin (const void *userbuf, void *kernelbuf, size_t cn);
>  extern int copyinmsg (const void *userbuf, void *kernelbuf, size_t cn, 
> size_t kn);
>  extern int copyout (const void *kernelbuf, void *userbuf, size_t cn);
> +#ifdef USER32
>  extern int copyoutmsg (const void *kernelbuf, void *userbuf, size_t cn);
> +#else
> +static inline int copyoutmsg (const void *kernelbuf, void *userbuf, size_t 
> cn) {
> +     return copyout (kernelbuf, userbuf, cn);
> +}
> +#endif
>  
>  extern int inst_fetch (int eip, int cs);
>  
> diff --git a/ipc/ipc_kmsg.c b/ipc/ipc_kmsg.c
> index bd84380..6a7ad3c 100644
> --- a/ipc/ipc_kmsg.c
> +++ b/ipc/ipc_kmsg.c
> @@ -1321,7 +1321,7 @@ ipc_kmsg_copyin_body(
>               mach_msg_type_number_t number;
>               boolean_t is_inline, longform, dealloc, is_port;
>               vm_offset_t data;
> -             uint64_t length;
> +             vm_size_t length;
>               kern_return_t kr;
>  
>               type = (mach_msg_type_long_t *) saddr;
> @@ -1355,7 +1355,8 @@ ipc_kmsg_copyin_body(
>  
>               is_port = MACH_MSG_TYPE_PORT_ANY(name);
>  
> -             if ((is_port && (size != PORT_T_SIZE_IN_BITS)) ||
> +             if ((is_port && !is_inline && (size != 
> PORT_NAME_T_SIZE_IN_BITS)) ||
> +                 (is_port && is_inline && (size != PORT_T_SIZE_IN_BITS)) ||
>  #ifndef __x86_64__
>                   (longform && ((type->msgtl_header.msgt_name != 0) ||
>                                 (type->msgtl_header.msgt_size != 0) ||
> @@ -1396,11 +1397,22 @@ ipc_kmsg_copyin_body(
>                       if (length == 0)
>                               data = 0;
>                       else if (is_port) {
> +                             const vm_size_t user_length = length;
> +                             /*
> +                              * In 64 bit architectures, out of line port 
> names are
> +                              * represented as an array of mach_port_name_t 
> which are
> +                              * smaller than mach_port_t.
> +                              */
> +                             if (sizeof(mach_port_name_t) != 
> sizeof(mach_port_t)) {
> +                                     length = sizeof(mach_port_t) * number;
> +                                     type->msgtl_size = sizeof(mach_port_t) 
> * 8;
> +                             }
> +
>                               data = kalloc(length);
>                               if (data == 0)
>                                       goto invalid_memory;
>  
> -                             if (sizeof(mach_port_name_t) != 
> sizeof(mach_port_t))
> +                             if (user_length != length)
>                               {
>                                       mach_port_name_t *src = 
> (mach_port_name_t*)addr;
>                                       mach_port_t *dst = (mach_port_t*)data;
> @@ -1416,7 +1428,7 @@ ipc_kmsg_copyin_body(
>                                       goto invalid_memory;
>                               }
>                               if (dealloc &&
> -                                 (vm_deallocate(map, addr, length) != 
> KERN_SUCCESS)) {
> +                                 (vm_deallocate(map, addr, user_length) != 
> KERN_SUCCESS)) {
>                                       kfree(data, length);
>                                       goto invalid_memory;
>                               }
> @@ -2372,7 +2384,7 @@ ipc_kmsg_copyout_body(
>               mach_msg_type_size_t size;
>               mach_msg_type_number_t number;
>               boolean_t is_inline, longform, is_port;
> -             uint64_t length;
> +             vm_size_t length;
>               vm_offset_t addr;
>  
>               type = (mach_msg_type_long_t *) saddr;
> @@ -2406,18 +2418,28 @@ ipc_kmsg_copyout_body(
>                       ipc_object_t *objects;
>                       mach_msg_type_number_t i;
>  
> -                     if (!is_inline && (length != 0)) {
> -                             /* first allocate memory in the map */
> -                             uint64_t allocated = length;
> +                     if (!is_inline) {
> +                             if (length != 0) {
> +                                     vm_size_t user_length = length;
>  
> -                             _Static_assert(sizeof(mach_port_name_t) <= 
> sizeof(mach_port_t),
> -                                             "Size of mach_port_t should be 
> equal or larger than mach_port_name_t.");
> -                             allocated -= (sizeof(mach_port_t) - 
> sizeof(mach_port_name_t)) * number;
> +                                     if (sizeof(mach_port_name_t) != 
> sizeof(mach_port_t)) {
> +                                             user_length = 
> sizeof(mach_port_name_t) * number;
> +                                     }
>  
> -                             kr = vm_allocate(map, &addr, allocated, TRUE);
> -                             if (kr != KERN_SUCCESS) {
> -                                     ipc_kmsg_clean_body(taddr, saddr);
> -                                     goto vm_copyout_failure;
> +                                     /* first allocate memory in the map */
> +                                     kr = vm_allocate(map, &addr, 
> user_length, TRUE);
> +                                     if (kr != KERN_SUCCESS) {
> +                                             ipc_kmsg_clean_body(taddr, 
> saddr);
> +                                             goto vm_copyout_failure;
> +                                     }
> +                             }
> +
> +                             if (sizeof(mach_port_name_t) != 
> sizeof(mach_port_t)) {
> +                                     /* Out of line ports are always 
> returned as mach_port_name_t.
> +                                      * Note: we have to do this after 
> ipc_kmsg_clean_body, otherwise
> +                                      * the cleanup function will not work 
> correctly.
> +                                      */
> +                                     type->msgtl_size = 
> sizeof(mach_port_name_t) * 8;
>                               }
>                       }
>  
> diff --git a/x86_64/copy_user.c b/x86_64/copy_user.c
> index c6e125d..89acd81 100644
> --- a/x86_64/copy_user.c
> +++ b/x86_64/copy_user.c
> @@ -49,10 +49,6 @@ typedef struct {
>    natural_t msgtl_number;
>  } mach_msg_user_type_long_t;
>  _Static_assert(sizeof(mach_msg_user_type_long_t) == 12);
> -#else
> -typedef mach_msg_type_t mach_msg_user_type_t;
> -typedef mach_msg_type_long_t mach_msg_user_type_long_t;
> -#endif  /* USER32 */
>  
>  /*
>  * Helper to unpack the relevant fields of a msg type; the fields are 
> different
> @@ -87,7 +83,6 @@ static inline void unpack_msg_type(vm_offset_t addr,
>      }
>  }
>  
> -#ifdef USER32
>  static inline void mach_msg_user_type_to_kernel(const mach_msg_user_type_t 
> *u,
>      mach_msg_type_t* k) {
>    k->msgt_name = u->msgt_name;
> @@ -144,53 +139,33 @@ static inline void 
> mach_msg_kernel_type_to_user_long(const mach_msg_type_long_t
>    };
>    *u = user;
>  }
> -#endif
>  
>  static inline int copyin_mach_msg_type(const rpc_vm_offset_t *uaddr, 
> mach_msg_type_t *kaddr) {
> -#ifdef USER32
>    mach_msg_user_type_t user;
> -  int ret = copyin(uaddr, &user, sizeof(mach_msg_user_type_t));
> -  if (ret) {
> -    return ret;
> -  }
> +  if (copyin(uaddr, &user, sizeof(mach_msg_user_type_t)))
> +    return 1;
>    mach_msg_user_type_to_kernel(&user, kaddr);
>    return 0;
> -#else
> -  return copyin(uaddr, kaddr, sizeof(mach_msg_type_t));
> -#endif
>  }
>  
>  static inline int copyout_mach_msg_type(const mach_msg_type_t *kaddr, 
> rpc_vm_offset_t  *uaddr) {
> -#ifdef USER32
>    mach_msg_user_type_t user;
>    mach_msg_kernel_type_to_user(kaddr, &user);
>    return copyout(&user, uaddr, sizeof(mach_msg_user_type_t));
> -#else
> -  return copyout(kaddr, uaddr, sizeof(mach_msg_type_t));
> -#endif
>  }
>  
>  static inline int copyin_mach_msg_type_long(const rpc_vm_offset_t *uaddr, 
> mach_msg_type_long_t *kaddr) {
> -#ifdef USER32
>    mach_msg_user_type_long_t user;
> -  int ret = copyin(uaddr, &user, sizeof(mach_msg_user_type_long_t));
> -  if (ret)
> -    return ret;
> +  if (copyin(uaddr, &user, sizeof(mach_msg_user_type_long_t)))
> +    return 1;
>    mach_msg_user_type_to_kernel_long(&user, kaddr);
>    return 0;
> -#else
> -  return copyin(uaddr, kaddr, sizeof(mach_msg_type_long_t));
> -#endif
>  }
>  
>  static inline int copyout_mach_msg_type_long(const mach_msg_type_long_t 
> *kaddr, rpc_vm_offset_t *uaddr) {
> -#ifdef USER32
>    mach_msg_user_type_long_t user;
>    mach_msg_kernel_type_to_user_long(kaddr, &user);
>    return copyout(&user, uaddr, sizeof(mach_msg_user_type_long_t));
> -#else
> -  return copyout(kaddr, uaddr, sizeof(mach_msg_type_long_t));
> -#endif
>  }
>  
>  /* Optimized version of unpack_msg_type(), including proper copyin() */
> @@ -265,15 +240,8 @@ static inline int copyout_unpack_msg_type(vm_offset_t 
> kaddr,
>        mach_msg_type_size_t orig_size = kmtl->msgtl_size;
>        int ret;
>  
> -      if (MACH_MSG_TYPE_PORT_ANY(kmtl->msgtl_name)) {
> -#ifdef USER32
> -        kmtl->msgtl_size = bytes_to_descsize(sizeof(mach_port_name_t));
> -#else
> -        /* 64 bit ABI uses mach_port_name_inlined_t for inlined ports. */
> -        if (!kmt->msgt_inline)
> +      if (MACH_MSG_TYPE_PORT_ANY(kmtl->msgtl_name) && kmt->msgt_inline)
>            kmtl->msgtl_size = bytes_to_descsize(sizeof(mach_port_name_t));
> -#endif
> -      }
>        ret = copyout_mach_msg_type_long(kmtl, (void*)uaddr);
>        kmtl->msgtl_size = orig_size;
>        if (ret)
> @@ -289,21 +257,12 @@ static inline int copyout_unpack_msg_type(vm_offset_t 
> kaddr,
>      {
>        mach_msg_type_size_t orig_size = kmt->msgt_size;
>        int ret;
> -
> -      if (MACH_MSG_TYPE_PORT_ANY(kmt->msgt_name)) {
> -#ifdef USER32
> +      if (MACH_MSG_TYPE_PORT_ANY(kmt->msgt_name) && kmt->msgt_inline)
>          kmt->msgt_size = bytes_to_descsize(sizeof(mach_port_name_t));
> -#else
> -        /* 64 bit ABI uses mach_port_name_inlined_t for inlined ports. */
> -        if (!kmt->msgt_inline)
> -          kmt->msgt_size = bytes_to_descsize(sizeof(mach_port_name_t));
> -#endif
> -      }
>        ret = copyout_mach_msg_type(kmt, (void *)uaddr);
>        kmt->msgt_size = orig_size;
>        if (ret)
>          return 1;
> -
>        *name = kmt->msgt_name;
>        *size = kmt->msgt_size;
>        *number = kmt->msgt_number;
> @@ -313,7 +272,6 @@ static inline int copyout_unpack_msg_type(vm_offset_t 
> kaddr,
>    return 0;
>  }
>  
> -#ifdef USER32
>  /*
>   * Compute the user-space size of a message still in the kernel when 
> processing
>   * messages from 32bit userland.
> @@ -372,7 +330,7 @@ size_t msg_usize(const mach_msg_header_t *kmsg)
>      }
>    return usize;
>  }
> -#endif /* USER32 */
> +#endif  /* USER32 */
>  
>  /*
>   * Expand the msg header and, if required, the msg body (ports, pointers)
> @@ -386,6 +344,9 @@ int copyinmsg (const void *userbuf, void *kernelbuf, 
> const size_t usize, const s
>    const mach_msg_user_header_t *umsg = userbuf;
>    mach_msg_header_t *kmsg = kernelbuf;
>  
> +  
> _Static_assert(!mach_msg_user_is_misaligned(sizeof(mach_msg_user_header_t)),
> +                 "mach_msg_user_header_t needs to be MACH_MSG_USER_ALIGNMENT 
> aligned.");
> +
>  #ifdef USER32
>    if (copyin(&umsg->msgh_bits, &kmsg->msgh_bits, sizeof(kmsg->msgh_bits)))
>      return 1;
> @@ -397,23 +358,12 @@ int copyinmsg (const void *userbuf, void *kernelbuf, 
> const size_t usize, const s
>    if (copyin(&umsg->msgh_seqno, &kmsg->msgh_seqno,
>               sizeof(kmsg->msgh_seqno) + sizeof(kmsg->msgh_id)))
>      return 1;
> -#else
> -  /* The 64 bit interface ensures the header is the same size, so it does 
> not need any resizing. */
> -  _Static_assert(sizeof(mach_msg_header_t) == sizeof(mach_msg_user_header_t),
> -              "mach_msg_header_t and mach_msg_user_header_t expected to be 
> of the same size");
> -  if (copyin(umsg, kmsg, sizeof(mach_msg_header_t)))
> -    return 1;
> -  kmsg->msgh_remote_port &= 0xFFFFFFFF; // FIXME: still have port names here
> -  kmsg->msgh_local_port &= 0xFFFFFFFF;  // also, this assumes little-endian
> -#endif
>  
>    vm_offset_t usaddr, ueaddr, ksaddr;
>    ksaddr = (vm_offset_t)(kmsg + 1);
>    usaddr = (vm_offset_t)(umsg + 1);
>    ueaddr = (vm_offset_t)umsg + usize;
>  
> -  
> _Static_assert(!mach_msg_user_is_misaligned(sizeof(mach_msg_user_header_t)),
> -                 "mach_msg_user_header_t needs to be MACH_MSG_USER_ALIGNMENT 
> aligned.");
>  
>    if (usize > sizeof(mach_msg_user_header_t))
>      {
> @@ -441,7 +391,6 @@ int copyinmsg (const void *userbuf, void *kernelbuf, 
> const size_t usize, const s
>              {
>                if (MACH_MSG_TYPE_PORT_ANY(name))
>                  {
> -#ifdef USER32
>                    if (size != bytes_to_descsize(sizeof(mach_port_name_t)))
>                      return 1;
>                    if ((usaddr + sizeof(mach_port_name_t)*number) > ueaddr)
> @@ -454,17 +403,6 @@ int copyinmsg (const void *userbuf, void *kernelbuf, 
> const size_t usize, const s
>                        ksaddr += sizeof(mach_port_t);
>                        usaddr += sizeof(mach_port_name_t);
>                      }
> -#else
> -                  if (size != 
> bytes_to_descsize(sizeof(mach_port_name_inlined_t)))
> -                    return 1;
> -                  const vm_size_t length = number * 
> sizeof(mach_port_name_inlined_t);
> -                  if ((usaddr + length) > ueaddr)
> -                    return 1;
> -                  if (copyin((void*)usaddr, (void*)ksaddr, length))
> -                    return 1;
> -                  usaddr += length;
> -                  ksaddr += length;
> -#endif
>                  }
>                else
>                  {
> @@ -485,11 +423,9 @@ int copyinmsg (const void *userbuf, void *kernelbuf, 
> const size_t usize, const s
>  
>                /* out-of-line port arrays are always arrays of 
> mach_port_name_t (4 bytes)
>                 * and are expanded in ipc_kmsg_copyin_body() */
> -              if (MACH_MSG_TYPE_PORT_ANY(name)) {
> -                if (size != bytes_to_descsize(sizeof(mach_port_name_t)))
> -                  return 1;
> -                adjust_msg_type_size(ktaddr, sizeof(mach_port_t) - 
> sizeof(mach_port_name_t));
> -              }
> +              if (MACH_MSG_TYPE_PORT_ANY(name) &&
> +                  (size != bytes_to_descsize(sizeof(mach_port_name_t))))
> +                return 1;
>  
>                if (copyin_address((rpc_vm_offset_t*)usaddr, 
> (vm_offset_t*)ksaddr))
>                  return 1;
> @@ -506,18 +442,24 @@ int copyinmsg (const void *userbuf, void *kernelbuf, 
> const size_t usize, const s
>  
>    kmsg->msgh_size = sizeof(mach_msg_header_t) + ksaddr - (vm_offset_t)(kmsg 
> + 1);
>    assert(kmsg->msgh_size <= ksize);
> -#ifndef USER32
> -  if (kmsg->msgh_size != usize)
> +#else
> +  /* The 64 bit interface ensures the header is the same size, so it does 
> not need any resizing. */
> +  _Static_assert(sizeof(mach_msg_header_t) == sizeof(mach_msg_user_header_t),
> +              "mach_msg_header_t and mach_msg_user_header_t expected to be 
> of the same size");
> +  if (copyin(umsg, kmsg, usize))
>      return 1;
> +  kmsg->msgh_remote_port &= 0xFFFFFFFF; // FIXME: still have port names here
> +  kmsg->msgh_local_port &= 0xFFFFFFFF;  // also, this assumes little-endian
>  #endif
>    return 0;
>  }
>  
> +#ifdef USER32
> +/* This is defined simply as copyout for !USER32.  */
>  int copyoutmsg (const void *kernelbuf, void *userbuf, const size_t ksize)
>  {
>    const mach_msg_header_t *kmsg = kernelbuf;
>    mach_msg_user_header_t *umsg = userbuf;
> -#ifdef USER32
>    if (copyout(&kmsg->msgh_bits, &umsg->msgh_bits, sizeof(kmsg->msgh_bits)))
>      return 1;
>    /* umsg->msgh_size is filled in later */
> @@ -528,10 +470,6 @@ int copyoutmsg (const void *kernelbuf, void *userbuf, 
> const size_t ksize)
>    if (copyout(&kmsg->msgh_seqno, &umsg->msgh_seqno,
>               sizeof(kmsg->msgh_seqno) + sizeof(kmsg->msgh_id)))
>      return 1;
> -#else
> -  if (copyout(kmsg, umsg, sizeof(mach_msg_header_t)))
> -    return 1;
> -#endif  /* USER32 */
>  
>    vm_offset_t ksaddr, keaddr, usaddr;
>    ksaddr = (vm_offset_t)(kmsg + 1);
> @@ -559,7 +497,6 @@ int copyoutmsg (const void *kernelbuf, void *userbuf, 
> const size_t ksize)
>              {
>                if (MACH_MSG_TYPE_PORT_ANY(name))
>                  {
> -#ifdef USER32
>                    for (int i=0; i<number; i++)
>                      {
>                        if (copyout_port((mach_port_t*)ksaddr, 
> (mach_port_name_t*)usaddr))
> @@ -567,15 +504,6 @@ int copyoutmsg (const void *kernelbuf, void *userbuf, 
> const size_t ksize)
>                        ksaddr += sizeof(mach_port_t);
>                        usaddr += sizeof(mach_port_name_t);
>                      }
> -#else
> -                  if (size != 
> bytes_to_descsize(sizeof(mach_port_name_inlined_t)))
> -                    return 1;
> -                  const vm_size_t length = number * 
> sizeof(mach_port_name_inlined_t);
> -                  if (copyout((void*)ksaddr, (void*)usaddr, length))
> -                    return 1;
> -                  ksaddr += length;
> -                  usaddr += length;
> -#endif
>                  }
>                else
>                  {
> @@ -603,11 +531,6 @@ int copyoutmsg (const void *kernelbuf, void *userbuf, 
> const size_t ksize)
>    usize = sizeof(mach_msg_user_header_t) + usaddr - (vm_offset_t)(umsg + 1);
>    if (copyout(&usize, &umsg->msgh_size, sizeof(umsg->msgh_size)))
>      return 1;
> -#ifndef USER32
> -  if (usize != ksize)
> -    return 1;
> -#endif
> -
>    return 0;
> -
>  }
> +#endif  /* USER32 */
> -- 
> 2.39.2
> 
> 

-- 
Samuel
---
Pour une évaluation indépendante, transparente et rigoureuse !
Je soutiens la Commission d'Évaluation de l'Inria.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]