semaphore.c 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /*
  2. * linux/arch/s390/kernel/semaphore.c
  3. *
  4. * S390 version
  5. * Copyright (C) 1998-2000 IBM Corporation
  6. * Author(s): Martin Schwidefsky
  7. *
  8. * Derived from "linux/arch/i386/kernel/semaphore.c
  9. * Copyright (C) 1999, Linus Torvalds
  10. *
  11. */
  12. #include <linux/sched.h>
  13. #include <linux/errno.h>
  14. #include <linux/init.h>
  15. #include <asm/semaphore.h>
  16. /*
  17. * Atomically update sem->count. Equivalent to:
  18. * old_val = sem->count.counter;
  19. * new_val = ((old_val >= 0) ? old_val : 0) + incr;
  20. * sem->count.counter = new_val;
  21. * return old_val;
  22. */
  23. static inline int __sem_update_count(struct semaphore *sem, int incr)
  24. {
  25. int old_val, new_val;
  26. asm volatile(
  27. " l %0,0(%3)\n"
  28. "0: ltr %1,%0\n"
  29. " jhe 1f\n"
  30. " lhi %1,0\n"
  31. "1: ar %1,%4\n"
  32. " cs %0,%1,0(%3)\n"
  33. " jl 0b\n"
  34. : "=&d" (old_val), "=&d" (new_val), "=m" (sem->count)
  35. : "a" (&sem->count), "d" (incr), "m" (sem->count)
  36. : "cc");
  37. return old_val;
  38. }
  39. /*
  40. * The inline function up() incremented count but the result
  41. * was <= 0. This indicates that some process is waiting on
  42. * the semaphore. The semaphore is free and we'll wake the
  43. * first sleeping process, so we set count to 1 unless some
  44. * other cpu has called up in the meantime in which case
  45. * we just increment count by 1.
  46. */
  47. void __up(struct semaphore *sem)
  48. {
  49. __sem_update_count(sem, 1);
  50. wake_up(&sem->wait);
  51. }
  52. /*
  53. * The inline function down() decremented count and the result
  54. * was < 0. The wait loop will atomically test and update the
  55. * semaphore counter following the rules:
  56. * count > 0: decrement count, wake up queue and exit.
  57. * count <= 0: set count to -1, go to sleep.
  58. */
  59. void __sched __down(struct semaphore * sem)
  60. {
  61. struct task_struct *tsk = current;
  62. DECLARE_WAITQUEUE(wait, tsk);
  63. __set_task_state(tsk, TASK_UNINTERRUPTIBLE);
  64. add_wait_queue_exclusive(&sem->wait, &wait);
  65. while (__sem_update_count(sem, -1) <= 0) {
  66. schedule();
  67. set_task_state(tsk, TASK_UNINTERRUPTIBLE);
  68. }
  69. remove_wait_queue(&sem->wait, &wait);
  70. __set_task_state(tsk, TASK_RUNNING);
  71. wake_up(&sem->wait);
  72. }
  73. /*
  74. * Same as __down() with an additional test for signals.
  75. * If a signal is pending the count is updated as follows:
  76. * count > 0: wake up queue and exit.
  77. * count <= 0: set count to 0, wake up queue and exit.
  78. */
  79. int __sched __down_interruptible(struct semaphore * sem)
  80. {
  81. int retval = 0;
  82. struct task_struct *tsk = current;
  83. DECLARE_WAITQUEUE(wait, tsk);
  84. __set_task_state(tsk, TASK_INTERRUPTIBLE);
  85. add_wait_queue_exclusive(&sem->wait, &wait);
  86. while (__sem_update_count(sem, -1) <= 0) {
  87. if (signal_pending(current)) {
  88. __sem_update_count(sem, 0);
  89. retval = -EINTR;
  90. break;
  91. }
  92. schedule();
  93. set_task_state(tsk, TASK_INTERRUPTIBLE);
  94. }
  95. remove_wait_queue(&sem->wait, &wait);
  96. __set_task_state(tsk, TASK_RUNNING);
  97. wake_up(&sem->wait);
  98. return retval;
  99. }