#ifndef _SPL_RWLOCK_H #define _SPL_RWLOCK_H #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif typedef enum { RW_DRIVER = 2, /* driver (DDI) rwlock */ RW_DEFAULT = 4 /* kernel default rwlock */ } krw_type_t; typedef enum { RW_WRITER, RW_READER } krw_t; #define RW_READ_HELD(x) (__rw_read_held((x))) #define RW_WRITE_HELD(x) (__rw_write_held((x))) #define RW_LOCK_HELD(x) (__rw_lock_held((x))) #define RW_ISWRITER(x) (__rw_iswriter(x)) #define RW_MAGIC 0x3423645a #define RW_POISON 0xa6 typedef struct { int rw_magic; char *rw_name; struct rw_semaphore rw_sem; struct task_struct *rw_owner; /* holder of the write lock */ } krwlock_t; #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 inline 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 fastcall __up_read_locked(struct rw_semaphore *sem) { if (--sem->activity == 0 && !list_empty(&sem->wait_list)) sem = __rwsem_wake_one_writer_locked(sem); } /* * trylock for writing -- returns 1 if successful, 0 if contention */ static int fastcall __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 extern int __rw_read_held(krwlock_t *rwlp); extern int __rw_write_held(krwlock_t *rwlp); extern int __rw_lock_held(krwlock_t *rwlp); static __inline__ void rw_init(krwlock_t *rwlp, char *name, krw_type_t type, void *arg) { 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; /* no one holds the write lock yet */ init_rwsem(&rwlp->rw_sem); rwlp->rw_name = NULL; if (name) { rwlp->rw_name = kmalloc(strlen(name) + 1, GFP_KERNEL); if (rwlp->rw_name) strcpy(rwlp->rw_name, name); } } static __inline__ 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); if (rwlp->rw_name) kfree(rwlp->rw_name); memset(rwlp, RW_POISON, sizeof(krwlock_t)); } /* Return 0 if the lock could not be obtained without blocking. */ static __inline__ int rw_tryenter(krwlock_t *rwlp, krw_t rw) { int result; 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 */ result = down_read_trylock(&rwlp->rw_sem); break; case RW_WRITER: result = down_write_trylock(&rwlp->rw_sem); if (result) { /* there better not be anyone else * holding the write lock here */ ASSERT(rwlp->rw_owner == NULL); rwlp->rw_owner = current; } break; default: SBUG(); } return result; } static __inline__ void rw_enter(krwlock_t *rwlp, krw_t rw) { 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(); } } static __inline__ void rw_exit(krwlock_t *rwlp) { 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); } } static __inline__ void rw_downgrade(krwlock_t *rwlp) { ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); ASSERT(rwlp->rw_owner == current); rwlp->rw_owner = NULL; downgrade_write(&rwlp->rw_sem); } /* Return 0 if unable to perform the upgrade. * Might be wise to fix the caller * to acquire the write lock first? */ static __inline__ int rw_tryupgrade(krwlock_t *rwlp) { int result = 0; 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 */ result = __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 */ result = down_write_trylock(&rwlp->rw_sem); #endif /* Check if upgrade failed. Should not ever happen * if we got to this point */ ASSERT(result); ASSERT(rwlp->rw_owner == NULL); rwlp->rw_owner = current; spin_unlock(&rwlp->rw_sem.wait_lock); return 1; } static __inline__ kthread_t * rw_owner(krwlock_t *rwlp) { ASSERT(rwlp); ASSERT(rwlp->rw_magic == RW_MAGIC); return rwlp->rw_owner; } #ifdef __cplusplus } #endif #endif /* _SPL_RWLOCK_H */