]> git.hungrycats.org Git - linux/commitdiff
[PATCH] m32r: fix sys_tas system call for m32r
authorHirokazu Takata <takata@linux-m32r.org>
Mon, 18 Oct 2004 16:05:18 +0000 (09:05 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Mon, 18 Oct 2004 16:05:18 +0000 (09:05 -0700)
This patch fixes a sys_tas system call for m32r.

- This patch fixes an Oops at sys_tas() in case CONFIG_SMP && CONFIG_PREEMPT.
  > Unable to handle kernel paging request at virtual address XXXXXXXX

  It is because a page fault happens at the spin_locked region in sys_tas()
  and in_atomic() checks preempt_count, but spin_lock() already counts up
  the preemt_count.

  arch/m32r/kernel/sys_m32r.c:
    32  /*
    33   * sys_tas() - test-and-set
    34   * linuxthreads testing version
    35   */
    36  #ifndef CONFIG_SMP
    37  asmlinkage int sys_tas(int *addr)
    38  {
    39          int oldval;
    40          unsigned long flags;
    41
    42          if (!access_ok(VERIFY_WRITE, addr, sizeof (int)))
    43                  return -EFAULT;
    44          local_irq_save(flags);
    45          oldval = *addr;
    46          *addr = 1;
    47          local_irq_restore(flags);
    48          return oldval;
    49  }
    50  #else /* CONFIG_SMP */
    51  #include <linux/spinlock.h>
    52
    53  static spinlock_t tas_lock = SPIN_LOCK_UNLOCKED;
    54
    55  asmlinkage int sys_tas(int *addr)
    56  {
    57          int oldval;
    58
    59          if (!access_ok(VERIFY_WRITE, addr, sizeof (int)))
    60                  return -EFAULT;
    61
    62          spin_lock(&tas_lock);
    63          oldval = *addr;

/* <<< ATTENTION >>>
 * A page fault may happen here, because "addr" points an
 * user-space area.
 */

    64          *addr = 1;
    65          spin_unlock(&tas_lock);
    66
    67          return oldval;
    68  }
    69  #endif /* CONFIG_SMP */

  arch/mm/fault.c:
   137  /*
   138   * If we're in an interrupt or have no user context or are runni
ng in an
   139   * atomic region then we must not take the fault..
   140   */
   141  if (in_atomic() || !mm)
   142          goto bad_area_nosemaphore;

- sys_tas() is used for user-level mutual exclusion for the m32r,
  which is prepared to implement a linuxthreads library.
  The above problem may be happened in a program, which uses
  pthread_mutex_lock(), calls sys_tas().

  The current m32r instruction set has no user-level locking
  functions for mutual exclusion.
  # I hope it will be fixed in the future...

- This patch fixes the problem by using _raw_spin_lock() instead of
  spin_lock().  spin_lock() increments up preemt_count, on the contrary,
  _raw_sping_lock() does not.

  # I think this fix is just a temporary work around, and
  # it is preferable to be rewrite to make it simpler by using
  # asm() function or something...

* arch/m32r/kernel/sys_m32r.c:
- Fix sys_tas() for CONFIG_SMP && CONFIG_PREEMPT.

Signed-off-by: Hayato Fujiwara <fujiwara@linux-m32r.org>
Signed-off-by: Hirokazu Takata <takata@linux-m32r.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/m32r/kernel/sys_m32r.c

index f34fa19ac1e4b11b2193b207c669a22adade6887..435619e436522df8c85407abd97d5026a7ad858f 100644 (file)
@@ -57,10 +57,10 @@ asmlinkage int sys_tas(int *addr)
        if (!access_ok(VERIFY_WRITE, addr, sizeof (int)))
                return -EFAULT;
 
-       spin_lock(&tas_lock);
+       _raw_spin_lock(&tas_lock);
        oldval = *addr;
        *addr = 1;
-       spin_unlock(&tas_lock);
+       _raw_spin_unlock(&tas_lock);
 
        return oldval;
 }