/* * This file is part of the SPL: Solaris Porting Layer. * * Copyright (c) 2008 Lawrence Livermore National Security, LLC. * Produced at Lawrence Livermore National Laboratory * Written by: * Brian Behlendorf , * Herb Wartens , * Jim Garlick * UCRL-CODE-235197 * * This 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 of the License, or * (at your option) any later version. * * This 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #ifdef DEBUG_SUBSYSTEM #undef DEBUG_SUBSYSTEM #endif #define DEBUG_SUBSYSTEM S_RWLOCK #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK struct rwsem_waiter { struct list_head list; struct task_struct *task; unsigned int flags; #define RWSEM_WAITING_FOR_READ 0x00000001 #define RWSEM_WAITING_FOR_WRITE 0x00000002 }; /* wake a single writer */ static struct rw_semaphore * __rwsem_wake_one_writer_locked(struct rw_semaphore *sem) { struct rwsem_waiter *waiter; struct task_struct *tsk; sem->activity = -1; waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list); list_del(&waiter->list); tsk = waiter->task; smp_mb(); waiter->task = NULL; wake_up_process(tsk); put_task_struct(tsk); return sem; } /* release a read lock on the semaphore */ static void __up_read_locked(struct rw_semaphore *sem) { if (--sem->activity == 0 && !list_empty(&sem->wait_list)) (void)__rwsem_wake_one_writer_locked(sem); } /* trylock for writing -- returns 1 if successful, 0 if contention */ static int __down_write_trylock_locked(struct rw_semaphore *sem) { int ret = 0; if (sem->activity == 0 && list_empty(&sem->wait_list)) { /* granted */ sem->activity = -1; ret = 1; } return ret; } #endif void __rw_init(krwlock_t *rwlp, char *name, krw_type_t type, void *arg) { int flags = KM_SLEEP; ASSERT(rwlp); ASSERT(name); ASSERT(type == RW_DEFAULT); /* XXX no irq handler use */ ASSERT(arg == NULL); /* XXX no irq handler use */ rwlp->rw_magic = RW_MAGIC; rwlp->rw_owner = NULL; rwlp->rw_name = NULL; rwlp->rw_name_size = strlen(name) + 1; /* We may be called when there is a non-zero preempt_count or * interrupts are disabled is which case we must not sleep. */ if (current_thread_info()->preempt_count || irqs_disabled()) flags = KM_NOSLEEP; rwlp->rw_name = kmem_alloc(rwlp->rw_name_size, flags); if (rwlp->rw_name == NULL) return; init_rwsem(&rwlp->rw_sem); strcpy(rwlp->rw_name, name); } EXPORT_SYMBOL(__rw_init); void __rw_destroy(krwlock_t *rwlp) { ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); ASSERT(rwlp->rw_owner == NULL); spin_lock(&rwlp->rw_sem.wait_lock); ASSERT(list_empty(&rwlp->rw_sem.wait_list)); spin_unlock(&rwlp->rw_sem.wait_lock); kmem_free(rwlp->rw_name, rwlp->rw_name_size); memset(rwlp, RW_POISON, sizeof(krwlock_t)); } EXPORT_SYMBOL(__rw_destroy); /* Return 0 if the lock could not be obtained without blocking. */ int __rw_tryenter(krwlock_t *rwlp, krw_t rw) { int rc = 0; ENTRY; ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); switch (rw) { /* these functions return 1 if success, 0 if contention */ case RW_READER: /* Here the Solaris code would return 0 * if there were any write waiters. Specifically * thinking about the case where readers may have * the lock and we would also allow this thread * to grab the read lock with a writer waiting in the * queue. This doesn't seem like a correctness * issue, so just call down_read_trylock() * for the test. We may have to revisit this if * it becomes an issue */ rc = down_read_trylock(&rwlp->rw_sem); break; case RW_WRITER: rc = down_write_trylock(&rwlp->rw_sem); if (rc) { /* there better not be anyone else * holding the write lock here */ ASSERT(rwlp->rw_owner == NULL); rwlp->rw_owner = current; } break; default: SBUG(); } RETURN(rc); } EXPORT_SYMBOL(__rw_tryenter); void __rw_enter(krwlock_t *rwlp, krw_t rw) { ENTRY; ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); switch (rw) { case RW_READER: /* Here the Solaris code would block * if there were any write waiters. Specifically * thinking about the case where readers may have * the lock and we would also allow this thread * to grab the read lock with a writer waiting in the * queue. This doesn't seem like a correctness * issue, so just call down_read() * for the test. We may have to revisit this if * it becomes an issue */ down_read(&rwlp->rw_sem); break; case RW_WRITER: down_write(&rwlp->rw_sem); /* there better not be anyone else * holding the write lock here */ ASSERT(rwlp->rw_owner == NULL); rwlp->rw_owner = current; break; default: SBUG(); } EXIT; } EXPORT_SYMBOL(__rw_enter); void __rw_exit(krwlock_t *rwlp) { ENTRY; ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); /* rw_owner is held by current * thread iff it is a writer */ if (rwlp->rw_owner == current) { rwlp->rw_owner = NULL; up_write(&rwlp->rw_sem); } else { up_read(&rwlp->rw_sem); } EXIT; } EXPORT_SYMBOL(__rw_exit); void __rw_downgrade(krwlock_t *rwlp) { ENTRY; ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); ASSERT(rwlp->rw_owner == current); rwlp->rw_owner = NULL; downgrade_write(&rwlp->rw_sem); EXIT; } EXPORT_SYMBOL(__rw_downgrade); /* Return 0 if unable to perform the upgrade. * Might be wise to fix the caller * to acquire the write lock first? */ int __rw_tryupgrade(krwlock_t *rwlp) { int rc = 0; ENTRY; ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); spin_lock(&rwlp->rw_sem.wait_lock); /* Check if there is anyone waiting for the * lock. If there is, then we know we should * not try to upgrade the lock */ if (!list_empty(&rwlp->rw_sem.wait_list)) { spin_unlock(&rwlp->rw_sem.wait_lock); RETURN(0); } #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK /* Note that activity is protected by * the wait_lock. Don't try to upgrade * if there are multiple readers currently * holding the lock */ if (rwlp->rw_sem.activity > 1) { #else /* Don't try to upgrade * if there are multiple readers currently * holding the lock */ if ((rwlp->rw_sem.count & RWSEM_ACTIVE_MASK) > 1) { #endif spin_unlock(&rwlp->rw_sem.wait_lock); RETURN(0); } #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK /* Here it should be safe to drop the * read lock and reacquire it for writing since * we know there are no waiters */ __up_read_locked(&rwlp->rw_sem); /* returns 1 if success, 0 if contention */ rc = __down_write_trylock_locked(&rwlp->rw_sem); #else /* Here it should be safe to drop the * read lock and reacquire it for writing since * we know there are no waiters */ up_read(&rwlp->rw_sem); /* returns 1 if success, 0 if contention */ rc = down_write_trylock(&rwlp->rw_sem); #endif /* Check if upgrade failed. Should not ever happen * if we got to this point */ ASSERT(rc); ASSERT(rwlp->rw_owner == NULL); rwlp->rw_owner = current; spin_unlock(&rwlp->rw_sem.wait_lock); RETURN(1); } EXPORT_SYMBOL(__rw_tryupgrade); kthread_t * __rw_owner(krwlock_t *rwlp) { ENTRY; ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); RETURN(rwlp->rw_owner); } EXPORT_SYMBOL(__rw_owner); int __rw_read_held(krwlock_t *rwlp) { ENTRY; ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); RETURN(__rw_lock_held(rwlp) && rwlp->rw_owner == NULL); } EXPORT_SYMBOL(__rw_read_held); int __rw_write_held(krwlock_t *rwlp) { ENTRY; ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); RETURN(rwlp->rw_owner == current); } EXPORT_SYMBOL(__rw_write_held); int __rw_lock_held(krwlock_t *rwlp) { int rc = 0; ENTRY; ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); spin_lock_irq(&(rwlp->rw_sem.wait_lock)); #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK if (rwlp->rw_sem.activity != 0) { #else if (rwlp->rw_sem.count != 0) { #endif rc = 1; } spin_unlock_irq(&(rwlp->rw_sem.wait_lock)); RETURN(rc); } EXPORT_SYMBOL(__rw_lock_held);