[PATCH] interruptible rwsems

David Howells (dhowells@redhat.com)
Thu, 03 Oct 2002 10:17:39 +0100


Hi Linus,

Here's a patch to add interruptible down functions for rwsems (developed by
Murali N Vilayannur with help from myself).

David

diff -u linux-2.5.40/include/linux/rwsem.h linux-rwsem-2540/include/linux/rwsem.h
--- linux-2.5.40/include/linux/rwsem.h 2002-09-27 22:49:42.000000000 +0100
+++ linux-rwsem-2540/include/linux/rwsem.h 2002-10-02 09:01:13.000000000 +0100
@@ -2,6 +2,8 @@
*
* Written by David Howells (dhowells@redhat.com).
* Derived from asm-i386/semaphore.h
+ * - Trylock stuff by Brian J. Watson <Brian.J.Watson@compaq.com>
+ * - Interruptible stuff by Murali N Vilayannur <vilayann@cse.psu.edu>
*/

#ifndef _LINUX_RWSEM_H
@@ -59,6 +61,21 @@
}

/*
+ * lock for reading which can be interrupted.
+ * Returns 0 if we obtained the lock and -EINTR
+ * if we were interrupted and we did not obtain the lock.
+ */
+static inline int down_read_interruptible(struct rw_semaphore *sem)
+{
+ int ret;
+ might_sleep();
+ rwsemtrace(sem,"Entering down_read_interruptible");
+ ret = __down_read_interruptible(sem);
+ rwsemtrace(sem,"Leaving down_read_interruptible");
+ return ret;
+}
+
+/*
* lock for writing
*/
static inline void down_write(struct rw_semaphore *sem)
@@ -82,6 +99,21 @@
}

/*
+ * lock for writing which can be interrupted.
+ * Returns 0 if we obtained the lock and -EINTR
+ * if we were interrupted and we did not obtain the lock.
+ */
+static inline int down_write_interruptible(struct rw_semaphore *sem)
+{
+ int ret;
+ might_sleep();
+ rwsemtrace(sem,"Entering down_write_interruptible");
+ ret = __down_write_interruptible(sem);
+ rwsemtrace(sem,"Leaving down_write_interruptible");
+ return ret;
+}
+
+/*
* release a read lock
*/
static inline void up_read(struct rw_semaphore *sem)
diff -u linux-2.5.40/include/linux/rwsem-spinlock.h linux-rwsem-2540/include/linux/rwsem-spinlock.h
--- linux-2.5.40/include/linux/rwsem-spinlock.h 2002-09-27 22:50:19.000000000 +0100
+++ linux-rwsem-2540/include/linux/rwsem-spinlock.h 2002-10-02 09:01:18.000000000 +0100
@@ -3,6 +3,8 @@
* Copyright (c) 2001 David Howells (dhowells@redhat.com).
* - Derived partially from ideas by Andrea Arcangeli <andrea@suse.de>
* - Derived also from comments by Linus
+ * - Trylock stuff by Brian J. Watson <Brian.J.Watson@compaq.com>
+ * - Interruptible stuff by Murali N Vilayannur <vilayann@cse.psu.edu>
*/

#ifndef _LINUX_RWSEM_SPINLOCK_H
@@ -55,8 +57,10 @@
extern void FASTCALL(init_rwsem(struct rw_semaphore *sem));
extern void FASTCALL(__down_read(struct rw_semaphore *sem));
extern int FASTCALL(__down_read_trylock(struct rw_semaphore *sem));
+extern int FASTCALL(__down_read_interruptible(struct rw_semaphore *sem));
extern void FASTCALL(__down_write(struct rw_semaphore *sem));
extern int FASTCALL(__down_write_trylock(struct rw_semaphore *sem));
+extern int FASTCALL(__down_write_interruptible(struct rw_semaphore *sem));
extern void FASTCALL(__up_read(struct rw_semaphore *sem));
extern void FASTCALL(__up_write(struct rw_semaphore *sem));
extern void FASTCALL(__downgrade_write(struct rw_semaphore *sem));
diff -u linux-2.5.40/include/asm-i386/rwsem.h linux-rwsem-2540/include/asm-i386/rwsem.h
--- linux-2.5.40/include/asm-i386/rwsem.h 2002-09-27 22:49:49.000000000 +0100
+++ linux-rwsem-2540/include/asm-i386/rwsem.h 2002-10-02 09:07:38.000000000 +0100
@@ -1,6 +1,8 @@
/* rwsem.h: R/W semaphores implemented using XADD/CMPXCHG for i486+
*
* Written by David Howells (dhowells@redhat.com).
+ * - Trylock stuff by Brian J. Watson <Brian.J.Watson@compaq.com>
+ * - Interruptible stuff by Murali N Vilayannur <vilayann@cse.psu.edu>
*
* Derived from asm-i386/semaphore.h
*
@@ -45,6 +47,8 @@

extern struct rw_semaphore *FASTCALL(rwsem_down_read_failed(struct rw_semaphore *sem));
extern struct rw_semaphore *FASTCALL(rwsem_down_write_failed(struct rw_semaphore *sem));
+extern int FASTCALL(rwsem_down_read_interruptible_failed(struct rw_semaphore *sem));
+extern int FASTCALL(rwsem_down_write_interruptible_failed(struct rw_semaphore *sem));
extern struct rw_semaphore *FASTCALL(rwsem_wake(struct rw_semaphore *));
extern struct rw_semaphore *FASTCALL(rwsem_downgrade_wake(struct rw_semaphore *sem));

@@ -141,6 +145,37 @@
}

/*
+ * lock for reading which can be interrupted by a signal.
+ * Returns -EINTR on signal interruption and failure to get lock
+ * and 0 if we get the lock.
+ */
+
+static inline int __down_read_interruptible(struct rw_semaphore *sem)
+{
+ int result;
+ __asm__ __volatile__(
+ "# beginning down_read_interruptible\n\t"
+LOCK_PREFIX " incl (%%eax)\n\t" /* adds 0x00000001, returns the old value */
+ " js 2f\n\t" /* jump if we weren't granted the lock */
+ " xorl %%eax,%%eax\n\t" /* set return value to 0 */
+ "1:\n\t"
+ LOCK_SECTION_START("")
+ "2:\n\t"
+ " pushl %%ecx\n\t"
+ " pushl %%edx\n\t"
+ " call rwsem_down_read_interruptible_failed\n\t"
+ " popl %%edx\n\t"
+ " popl %%ecx\n\t"
+ " jmp 1b\n"
+ LOCK_SECTION_END
+ "# ending down_read_interruptible\n\t"
+ : "=a"(result), "=m"(sem->count)
+ : "a"(sem), "m"(sem->count)
+ : "memory", "cc");
+ return result;
+}
+
+/*
* lock for writing
*/
static inline void __down_write(struct rw_semaphore *sem)
@@ -181,6 +216,39 @@
}

/*
+ * lock for writing which can be interrupted by a signal,
+ * Returns -EINTR if we get interrupted by a signal and
+ * if we did not get the lock and 0 if we get the lock.
+ */
+
+static inline int __down_write_interruptible(struct rw_semaphore *sem)
+{
+ int result, tmp;
+
+ tmp = RWSEM_ACTIVE_WRITE_BIAS;
+ __asm__ __volatile__(
+ "# beginning down_write_interruptible\n\t"
+LOCK_PREFIX " xadd %%edx,(%%eax)\n\t" /* subtract 0x0000ffff, returns the old value */
+ " testl %%edx,%%edx\n\t" /* was the count 0 before? */
+ " jnz 2f\n\t" /* jump if we weren't granted the lock */
+ " xorl %%eax,%%eax\n\t" /* set return value to 0 */
+ "1:\n\t"
+ LOCK_SECTION_START("")
+ "2:\n\t"
+ " pushl %%ecx\n\t"
+ " call rwsem_down_write_interruptible_failed\n\t"
+ " popl %%ecx\n\t"
+ " jmp 1b\n"
+ LOCK_SECTION_END
+ "# ending down_write_interruptible"
+ : "=a"(result), "=d"(tmp), "=m"(sem->count)
+ : "a"(sem), "d"(tmp), "m"(sem->count)
+ : "memory", "cc");
+ return result;
+}
+
+
+/*
* unlock after reading
*/
static inline void __up_read(struct rw_semaphore *sem)
diff -ur linux-2.5.40/lib/rwsem.c linux-rwsem-2540/lib/rwsem.c
--- linux-2.5.40/lib/rwsem.c 2002-09-27 22:48:38.000000000 +0100
+++ linux-rwsem-2540/lib/rwsem.c 2002-10-02 09:00:26.000000000 +0100
@@ -2,6 +2,8 @@
*
* Written by David Howells (dhowells@redhat.com).
* Derived from arch/i386/kernel/semaphore.c
+ * - Trylock stuff by Brian J. Watson <Brian.J.Watson@compaq.com>
+ * - Interruptible stuff by Murali N Vilayannur <vilayann@cse.psu.edu>
*/
#include <linux/rwsem.h>
#include <linux/sched.h>
@@ -160,6 +162,66 @@
}

/*
+ * wait for a lock to be granted or to be interrupted
+ * by a signal.
+ * Returns -EINTR on interruption and if we did not get the lock
+ * and 0 if we got the lock.
+ */
+static inline int rwsem_down_interruptible_failed_common(struct rw_semaphore *sem,
+ struct rwsem_waiter *waiter,
+ signed long adjustment)
+{
+ int ret = 0;
+ struct task_struct *tsk = current;
+ signed long count;
+
+ set_task_state(tsk,TASK_INTERRUPTIBLE);
+
+ /* set up my own style of waitqueue */
+ spin_lock(&sem->wait_lock);
+ waiter->task = tsk;
+
+ list_add_tail(&waiter->list,&sem->wait_list);
+
+ /* note that we're now waiting on the lock, but no longer actively read-locking */
+ count = rwsem_atomic_update(adjustment,sem);
+
+ /* if there are no longer active locks, wake the front queued process(es) up
+ * - it might even be this process, since the waker takes a more active part
+ */
+ if (!(count & RWSEM_ACTIVE_MASK))
+ sem = __rwsem_do_wake(sem,1);
+
+ spin_unlock(&sem->wait_lock);
+
+ /* wait to be given the lock or to be interrupted */
+ for (;;) {
+ if (!waiter->flags)
+ break;
+ /* is there a signal pending for this task? */
+ if(signal_pending(tsk)) {
+ ret = -EINTR;
+ spin_lock(&sem->wait_lock);
+ /* Check if we have got the lock in the meantime. If so we still return 0 */
+ if(!waiter->flags) {
+ ret = 0;
+ }
+ else {
+ /* Undo the count and remove ourselves from the wait queues */
+ rwsem_atomic_add(-RWSEM_WAITING_BIAS,sem);
+ list_del(&waiter->list);
+ }
+ spin_unlock(&sem->wait_lock);
+ break;
+ }
+ schedule();
+ set_task_state(tsk, TASK_INTERRUPTIBLE);
+ }
+ tsk->state = TASK_RUNNING;
+ return ret;
+}
+
+/*
* wait for the read lock to be granted
*/
struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem)
@@ -176,6 +238,22 @@
}

/*
+ * wait for the read lock to be granted or to be interrupted by a signal.
+ * Return if interrupted and not holding the lock with -EINTR
+ * and 0 if we got hold of the lock.
+ */
+int rwsem_down_read_interruptible_failed(struct rw_semaphore *sem)
+{
+ int ret;
+ struct rwsem_waiter waiter;
+ rwsemtrace(sem,"Entering rwsem_down_read_interruptible_failed");
+ waiter.flags = RWSEM_WAITING_FOR_READ;
+ ret = rwsem_down_interruptible_failed_common(sem,&waiter,RWSEM_WAITING_BIAS-RWSEM_ACTIVE_BIAS);
+ rwsemtrace(sem,"Leaving rwsem_down_read_interruptible_failed");
+ return ret;
+}
+
+/*
* wait for the write lock to be granted
*/
struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem)
@@ -192,6 +270,22 @@
}

/*
+ * wait for the write lock to be granted or to be interrupted by a signal.
+ * Return if interrupted and if did not get hold of the lock with -EINTR
+ * and 0 if got hold of the lock.
+ */
+int rwsem_down_write_interruptible_failed(struct rw_semaphore *sem)
+{
+ int ret;
+ struct rwsem_waiter waiter;
+ rwsemtrace(sem,"Entering rwsem_down_write_interruptible_failed");
+ waiter.flags = RWSEM_WAITING_FOR_WRITE;
+ ret = rwsem_down_interruptible_failed_common(sem,&waiter,-RWSEM_ACTIVE_BIAS);
+ rwsemtrace(sem,"Leaving rwsem_down_write_interruptible_failed");
+ return ret;
+}
+
+/*
* handle waking up a waiter on the semaphore
* - up_read has decremented the active part of the count if we come here
*/
@@ -234,7 +328,9 @@
}

EXPORT_SYMBOL_NOVERS(rwsem_down_read_failed);
+EXPORT_SYMBOL_NOVERS(rwsem_down_read_interruptible_failed);
EXPORT_SYMBOL_NOVERS(rwsem_down_write_failed);
+EXPORT_SYMBOL_NOVERS(rwsem_down_write_interruptible_failed);
EXPORT_SYMBOL_NOVERS(rwsem_wake);
EXPORT_SYMBOL_NOVERS(rwsem_downgrade_wake);
#if RWSEM_DEBUG
diff -ur linux-2.5.40/lib/rwsem-spinlock.c linux-rwsem-2540/lib/rwsem-spinlock.c
--- linux-2.5.40/lib/rwsem-spinlock.c 2002-09-27 22:49:06.000000000 +0100
+++ linux-rwsem-2540/lib/rwsem-spinlock.c 2002-10-02 09:21:45.000000000 +0100
@@ -4,6 +4,8 @@
* Copyright (c) 2001 David Howells (dhowells@redhat.com).
* - Derived partially from idea by Andrea Arcangeli <andrea@suse.de>
* - Derived also from comments by Linus
+ * - Trylock stuff by Brian J. Watson <Brian.J.Watson@compaq.com>
+ * - Interruptible stuff by Murali N Vilayannur <vilayann@cse.psu.edu>
*/
#include <linux/rwsem.h>
#include <linux/sched.h>
@@ -182,6 +184,67 @@
}

/*
+ * get a read lock on the semaphore which can be interrupted by a signal.
+ * Returns -EINTR if we get interrupted and if we did not get the lock
+ * and 0 if we get the lock
+ */
+int __down_read_interruptible(struct rw_semaphore *sem)
+{
+ struct rwsem_waiter waiter;
+ struct task_struct *tsk;
+ int result = 0;
+
+ rwsemtrace(sem,"Entering __down_read_interruptible");
+
+ spin_lock(&sem->wait_lock);
+
+ if (sem->activity>=0 && list_empty(&sem->wait_list)) {
+ /* granted */
+ sem->activity++;
+ spin_unlock(&sem->wait_lock);
+ goto out;
+ }
+
+ tsk = current;
+ set_task_state(tsk,TASK_INTERRUPTIBLE);
+
+ /* set up my own style of waitqueue */
+ waiter.task = tsk;
+ waiter.flags = RWSEM_WAITING_FOR_READ;
+
+ list_add_tail(&waiter.list,&sem->wait_list);
+
+ /* we don't need to touch the semaphore struct anymore */
+ spin_unlock(&sem->wait_lock);
+
+ /* wait to be given the lock */
+ for (;;) {
+ if (!waiter.flags)
+ break;
+ /* Is there a signal pending for this task */
+ if(signal_pending(tsk)) {
+ result = -EINTR;
+ spin_lock(&sem->wait_lock);
+ /* was i given the lock in the meantime? might as well return 0 here */
+ if(!waiter.flags) {
+ result = 0;
+ }
+ else {
+ list_del(&waiter.list);
+ }
+ spin_unlock(&sem->wait_lock);
+ break;
+ }
+ schedule();
+ set_task_state(tsk, TASK_INTERRUPTIBLE);
+ }
+ tsk->state = TASK_RUNNING;
+out:
+ rwsemtrace(sem,"Leaving __down_read_interruptible");
+ return result;
+}
+
+/*
* get a write lock on the semaphore
* - note that we increment the waiting count anyway to indicate an exclusive lock
*/
@@ -250,6 +313,68 @@
}

/*
+ * get a write lock on the semaphore
+ * - note that we increment the waiting count anyway to indicate an exclusive lock
+ * We return -EINTR on a signal interruption and if we did not get the lock
+ * and 0 if we get the lock.
+ */
+int __down_write_interruptible(struct rw_semaphore *sem)
+{
+ struct rwsem_waiter waiter;
+ struct task_struct *tsk;
+ int result = 0;
+
+ rwsemtrace(sem,"Entering __down_write_interruptible");
+
+ spin_lock(&sem->wait_lock);
+
+ if (sem->activity==0 && list_empty(&sem->wait_list)) {
+ /* granted */
+ sem->activity = -1;
+ spin_unlock(&sem->wait_lock);
+ goto out;
+ }
+
+ tsk = current;
+ set_task_state(tsk,TASK_INTERRUPTIBLE);
+
+ /* set up my own style of waitqueue */
+ waiter.task = tsk;
+ waiter.flags = RWSEM_WAITING_FOR_WRITE;
+
+ list_add_tail(&waiter.list,&sem->wait_list);
+
+ /* we don't need to touch the semaphore struct anymore */
+ spin_unlock(&sem->wait_lock);
+
+ /* wait to be given the lock */
+ for (;;) {
+ if (!waiter.flags)
+ break;
+ /* Is there a signal pending for this task? */
+ if(signal_pending(tsk)) {
+ result = -EINTR;
+ spin_lock(&sem->wait_lock);
+ /* was i given the lock in the meantime? might as well return 0 here */
+ if(!waiter.flags) {
+ result = 0;
+ }
+ else {
+ list_del(&waiter.list);
+ }
+ spin_unlock(&sem->wait_lock);
+ break;
+ }
+ schedule();
+ set_task_state(tsk, TASK_INTERRUPTIBLE);
+ }
+ tsk->state = TASK_RUNNING;
+out:
+ rwsemtrace(sem,"Leaving __down_write_interruptible");
+ return result;
+}
+
+/*
* release a read lock on the semaphore
*/
void __up_read(struct rw_semaphore *sem)
@@ -306,8 +431,10 @@
EXPORT_SYMBOL(init_rwsem);
EXPORT_SYMBOL(__down_read);
EXPORT_SYMBOL(__down_read_trylock);
+EXPORT_SYMBOL(__down_read_interruptible);
EXPORT_SYMBOL(__down_write);
EXPORT_SYMBOL(__down_write_trylock);
+EXPORT_SYMBOL(__down_write_interruptible);
EXPORT_SYMBOL(__up_read);
EXPORT_SYMBOL(__up_write);
EXPORT_SYMBOL(__downgrade_write);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/