Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id ; Thu, 3 Oct 2002 05:12:25 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id ; Thu, 3 Oct 2002 05:12:25 -0400 Received: from dell-paw-3.cambridge.redhat.com ([195.224.55.237]:2808 "EHLO executor.cambridge.redhat.com") by vger.kernel.org with ESMTP id ; Thu, 3 Oct 2002 05:12:19 -0400 From: David Howells To: torvalds@transmeta.com Cc: Murali N Vilayannur , linux-kernel@vger.kernel.org Subject: [PATCH] interruptible rwsems User-Agent: EMH/1.14.1 SEMI/1.14.3 (Ushinoya) FLIM/1.14.3 (=?ISO-8859-4?Q?Unebigory=F2mae?=) APEL/10.3 Emacs/21.2 (i686-pc-linux-gnu) MULE/5.0 (SAKAKI) MIME-Version: 1.0 (generated by SEMI 1.14.3 - "Ushinoya") Content-Type: text/plain; charset=US-ASCII Date: Thu, 03 Oct 2002 10:17:39 +0100 Message-ID: <13734.1033636659@warthog.cambridge.redhat.com> Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 15074 Lines: 487 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 + * - Interruptible stuff by Murali N Vilayannur */ #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 * - Derived also from comments by Linus + * - Trylock stuff by Brian J. Watson + * - Interruptible stuff by Murali N Vilayannur */ #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 + * - Interruptible stuff by Murali N Vilayannur * * 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 + * - Interruptible stuff by Murali N Vilayannur */ #include #include @@ -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 * - Derived also from comments by Linus + * - Trylock stuff by Brian J. Watson + * - Interruptible stuff by Murali N Vilayannur */ #include #include @@ -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/