/* * Copyright (C) 2010-2012 ARM Limited. All rights reserved. * * This program is free software and is provided to you under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. * * A copy of the licence is included with the program, and can also be obtained from Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file mali_osk_locks.c * Implemenation of the OS abstraction layer for the kernel device driver */ #include #include #include #include #include "mali_osk.h" #include "mali_kernel_common.h" /* These are all the locks we implement: */ typedef enum { _MALI_OSK_INTERNAL_LOCKTYPE_SPIN, /* Mutex, implicitly non-interruptable, use spin_lock/spin_unlock */ _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ, /* Mutex, IRQ version of spinlock, use spin_lock_irqsave/spin_unlock_irqrestore */ _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX, /* Interruptable, use mutex_unlock()/down_interruptable() */ _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT, /* Non-Interruptable, use mutex_unlock()/down() */ _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW, /* Non-interruptable, Reader/Writer, use {mutex_unlock,down}{read,write}() */ /* Linux supports, but we do not support: * Non-Interruptable Reader/Writer spinlock mutexes - RW optimization will be switched off */ /* Linux does not support: * One-locks, of any sort - no optimization for this fact will be made. */ } _mali_osk_internal_locktype; struct _mali_osk_lock_t_struct { _mali_osk_internal_locktype type; unsigned long flags; union { spinlock_t spinlock; struct mutex mutex; struct rw_semaphore rw_sema; } obj; MALI_DEBUG_CODE( /** original flags for debug checking */ _mali_osk_lock_flags_t orig_flags; /* id of the thread currently holding this lock, 0 if no * threads hold it. */ u32 owner; /* what mode the lock was taken in */ _mali_osk_lock_mode_t mode; ); /* MALI_DEBUG_CODE */ }; _mali_osk_lock_t *_mali_osk_lock_init( _mali_osk_lock_flags_t flags, u32 initial, u32 order ) { _mali_osk_lock_t *lock = NULL; /* Validate parameters: */ /* Flags acceptable */ MALI_DEBUG_ASSERT( 0 == ( flags & ~(_MALI_OSK_LOCKFLAG_SPINLOCK | _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE | _MALI_OSK_LOCKFLAG_READERWRITER | _MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_ONELOCK )) ); /* Spinlocks are always non-interruptable */ MALI_DEBUG_ASSERT( (((flags & _MALI_OSK_LOCKFLAG_SPINLOCK) || (flags & _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ)) && (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE)) || !(flags & _MALI_OSK_LOCKFLAG_SPINLOCK)); /* Parameter initial SBZ - for future expansion */ MALI_DEBUG_ASSERT( 0 == initial ); lock = kmalloc(sizeof(_mali_osk_lock_t), GFP_KERNEL); if ( NULL == lock ) { return lock; } /* Determine type of mutex: */ /* defaults to interruptable mutex if no flags are specified */ if ( (flags & _MALI_OSK_LOCKFLAG_SPINLOCK) ) { /* Non-interruptable Spinlocks override all others */ lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_SPIN; spin_lock_init( &lock->obj.spinlock ); } else if ( (flags & _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ ) ) { lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ; lock->flags = 0; spin_lock_init( &lock->obj.spinlock ); } else if ( (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE) && (flags & _MALI_OSK_LOCKFLAG_READERWRITER) ) { lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW; init_rwsem( &lock->obj.rw_sema ); } else { /* Usual mutex types */ if ( (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE) ) { lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT; } else { lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX; } /* Initially unlocked */ mutex_init(&lock->obj.mutex); } #ifdef DEBUG /* Debug tracking of flags */ lock->orig_flags = flags; /* Debug tracking of lock owner */ lock->owner = 0; #endif /* DEBUG */ return lock; } #ifdef DEBUG u32 _mali_osk_lock_get_owner( _mali_osk_lock_t *lock ) { return lock->owner; } u32 _mali_osk_lock_get_mode( _mali_osk_lock_t *lock ) { return lock->mode; } #endif /* DEBUG */ _mali_osk_errcode_t _mali_osk_lock_wait( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode) { _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; /* Parameter validation */ MALI_DEBUG_ASSERT_POINTER( lock ); MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode || _MALI_OSK_LOCKMODE_RO == mode ); /* Only allow RO locks when the initial object was a Reader/Writer lock * Since information is lost on the internal locktype, we use the original * information, which is only stored when built for DEBUG */ MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode || (_MALI_OSK_LOCKMODE_RO == mode && (_MALI_OSK_LOCKFLAG_READERWRITER & lock->orig_flags)) ); switch ( lock->type ) { case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN: spin_lock(&lock->obj.spinlock); break; case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ: { unsigned long tmp_flags; spin_lock_irqsave(&lock->obj.spinlock, tmp_flags); lock->flags = tmp_flags; } break; case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX: if (mutex_lock_interruptible(&lock->obj.mutex)) { MALI_PRINT_ERROR(("Can not lock mutex\n")); err = _MALI_OSK_ERR_RESTARTSYSCALL; } break; case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT: mutex_lock(&lock->obj.mutex); break; case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW: if (mode == _MALI_OSK_LOCKMODE_RO) { down_read(&lock->obj.rw_sema); } else { down_write(&lock->obj.rw_sema); } break; default: /* Reaching here indicates a programming error, so you will not get here * on non-DEBUG builds */ MALI_DEBUG_PRINT_ERROR( ("Invalid internal lock type: %.8X", lock->type ) ); break; } #ifdef DEBUG /* This thread is now the owner of this lock */ if (_MALI_OSK_ERR_OK == err) { if (mode == _MALI_OSK_LOCKMODE_RW) { if (0 != lock->owner) { printk(KERN_ERR "%d: ERROR: Lock %p already has owner %d\n", _mali_osk_get_tid(), lock, lock->owner); dump_stack(); } lock->owner = _mali_osk_get_tid(); lock->mode = mode; } else /* mode == _MALI_OSK_LOCKMODE_RO */ { lock->mode = mode; } } #endif return err; } void _mali_osk_lock_signal( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode ) { /* Parameter validation */ MALI_DEBUG_ASSERT_POINTER( lock ); MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode || _MALI_OSK_LOCKMODE_RO == mode ); /* Only allow RO locks when the initial object was a Reader/Writer lock * Since information is lost on the internal locktype, we use the original * information, which is only stored when built for DEBUG */ MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode || (_MALI_OSK_LOCKMODE_RO == mode && (_MALI_OSK_LOCKFLAG_READERWRITER & lock->orig_flags)) ); #ifdef DEBUG /* make sure the thread releasing the lock actually was the owner */ if (mode == _MALI_OSK_LOCKMODE_RW) { if (_mali_osk_get_tid() != lock->owner) { printk(KERN_ERR "%d: ERROR: Lock %p owner was %d\n", _mali_osk_get_tid(), lock, lock->owner); dump_stack(); } /* This lock now has no owner */ lock->owner = 0; } /* else if (mode == _MALI_OSK_LOCKMODE_RO) Nothing to check */ #endif /* DEBUG */ switch ( lock->type ) { case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN: spin_unlock(&lock->obj.spinlock); break; case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ: spin_unlock_irqrestore(&lock->obj.spinlock, lock->flags); break; case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX: /* FALLTHROUGH */ case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT: mutex_unlock(&lock->obj.mutex); break; case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW: if (mode == _MALI_OSK_LOCKMODE_RO) { up_read(&lock->obj.rw_sema); } else { up_write(&lock->obj.rw_sema); } break; default: /* Reaching here indicates a programming error, so you will not get here * on non-DEBUG builds */ MALI_DEBUG_PRINT_ERROR( ("Invalid internal lock type: %.8X", lock->type ) ); break; } } void _mali_osk_lock_term( _mali_osk_lock_t *lock ) { /* Parameter validation */ MALI_DEBUG_ASSERT_POINTER( lock ); /* Linux requires no explicit termination of spinlocks, semaphores, or rw_semaphores */ kfree(lock); }