bug-hurd
[Top][All Lists]
Advanced

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

Re: memory_object_lock_request and memory_object_data_return fnord


From: Neal H Walfield
Subject: Re: memory_object_lock_request and memory_object_data_return fnord
Date: 27 Mar 2002 00:35:07 -0500
User-agent: Gnus/5.0808 (Gnus v5.8.8) Emacs/21.1

> I think we should certainly use vm_copy for whole-page copies in
> pager_memcpy because of the badly suboptimal behavior you've described.

I have cooked up the attached implementation.  I checked everything
but a few border cases -- I need to write a few more tests.  Perhaps,
I will get to it this weekend.  However, I am interested in some
feedback.

Thanks.


/* Fault-safe copy into or out of pager-backed memory.
   Copyright (C) 1996,97,99,2000,01,02 Free Software Foundation, Inc.
   Written by Roland McGrath.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, or (at
   your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

#include "pager.h"
#include <sys/mman.h>
#include <hurd/sigpreempt.h>
#include <assert.h>
#include <string.h>

/* Try to copy *SIZE bytes between the region OTHER points to
   and the region at OFFSET in the pager indicated by PAGER and MEMOBJ.
   If PROT is VM_PROT_READ, copying is from the pager to OTHER;
   if PROT contains VM_PROT_WRITE, copying is from OTHER into the pager.
   *SIZE is always filled in the actual number of bytes successfully copied.
   Returns an error code if the pager-backed memory faults;
   if there is no fault, returns 0 and *SIZE will be unchanged.  */
error_t
pager_memcpy (struct pager *pager, memory_object_t memobj,
              vm_offset_t offset, void *other, size_t *size,
              vm_prot_t prot)
{
  error_t err;
  size_t n = *size;

#define VMCOPY_WINDOW_DEFAULT_SIZE (16 * vm_page_size)
#define MEMCPY_WINDOW_DEFAULT_SIZE (8 * vm_page_size)
  vm_address_t window;
  vm_size_t window_size;

  error_t do_vm_copy (void)
    {
      assert ((offset & (vm_page_size - 1)) == 0);
      assert (((vm_address_t) other & (vm_page_size - 1)) == 0);

      do
        {
          window_size = VMCOPY_WINDOW_DEFAULT_SIZE > n
                        ? n : VMCOPY_WINDOW_DEFAULT_SIZE;
          
          err = vm_map (mach_task_self (), &window, window_size, 0, 1,
                        memobj, offset, 0, prot, prot, VM_INHERIT_NONE);
          if (err)
            return err;

          if (prot == VM_PROT_READ)
            err = vm_copy (mach_task_self (), window, window_size,
                           (vm_address_t) other);
          else
            err = vm_copy (mach_task_self (), (vm_address_t) other,
                           window_size, window);

          vm_deallocate (mach_task_self (), window, window_size);

          if (err)
            return err;

          other += window_size;
          offset += window_size;
          n -= window_size;
        }
      while (n >= vm_page_size);

      return 0;
    }

  error_t do_copy (struct hurd_signal_preemptor *preemptor)
    {
      error_t do_memcpy (size_t to_copy)
        {
          window_size = MEMCPY_WINDOW_DEFAULT_SIZE;

          do
            {
              size_t pageoff = offset & (vm_page_size - 1);

              if (window)
                /* Deallocate the old window.  */
                {
                  vm_deallocate (mach_task_self (), window, window_size);
                  window = 0;
                }

              /* Map in and copy a standard-sized window, unless that is
                 more than the total left to be copied.  */

              if (window_size > pageoff + n)
                window_size = pageoff + n;

              err = vm_map (mach_task_self (), &window, window_size, 0, 1,
                            memobj, n - pageoff, 0,
                            prot, prot, VM_INHERIT_NONE);
              if (err)
                return err;

              /* Realign the fault preemptor for the new mapping window.  */
              preemptor->first = window;
              preemptor->last = window + window_size;

              if (prot == VM_PROT_READ)
                memcpy (other, (const void *) window + pageoff,
                        window_size - pageoff);
              else
                memcpy ((void *) window + pageoff, other,
                        window_size - pageoff);
              
              offset += window_size - pageoff;
              other += window_size - pageoff;
              to_copy -= window_size - pageoff;
              n -=  window_size - pageoff;
            }
          while (to_copy > 0);
          
          return 0;
        }

      /* Can we use vm_copy?  */
      if ((((vm_address_t) other & (vm_page_size - 1))
           == (offset & (vm_page_size - 1)))
          && (n - vm_page_size
              >= vm_page_size - ((vm_address_t) other & (vm_page_size - 1))))
        /* 1) other and offset are aligned with repect to each other;
           and 2) we have at least one fully aligned page.  */
        {
          err = do_memcpy (vm_page_size
                           - ((vm_address_t) other & (vm_page_size - 1)));
          if (err)
            return err;
   
          assert (n > vm_page_size);
          err = do_vm_copy ();
          if (err || n == 0)
            /* We failed or we finished.  */
            return err;
        }

      return do_memcpy (n);
    }

  jmp_buf buf;
  void fault (int signo, long int sigcode, struct sigcontext *scp)
    {
      assert (scp->sc_error == EKERN_MEMORY_ERROR);
      err = pager_get_error (pager, sigcode - window + offset);
      n -= sigcode - window;
      longjmp (buf, 1);
    }

  if (n == 0)
    /* Nothing to do.  */
    return 0;

  if (((vm_address_t) other & (vm_page_size - 1)) == 0
      && n >= vm_page_size
      && (((vm_address_t) other & (vm_page_size - 1))
          == (offset & (vm_page_size -1))))
    /* 1) the start address is page aligned; 2) we have more than a
       page; and 3) the source and destination are aligned with
       repect to each other.  */
    {
      err = do_vm_copy ();
      if (err || n == 0)
        /* We failed or we finished.  */
        {
          *size -= n;
          return err;
        }
    }

  /* Need to do it the hard way.  */

  window = 0;
  window_size = 0;

  if (setjmp (buf) == 0)
    hurd_catch_signal (sigmask (SIGSEGV) | sigmask (SIGBUS),
                       window, window + window_size,
                       &do_copy, (sighandler_t) &fault);

  if (window)
    vm_deallocate (mach_task_self (), window, window_size);

  if (! err)
    assert (n == 0);

  *size -= n;

  return err;
}


  
  



reply via email to

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