2 * Copyright (C) 2011-2012 ARM Limited. All rights reserved.
4 * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5 * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
7 * A copy of the licence is included with the program, and can also be obtained from Free Software
8 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 * @file mali_osk_locks.c
13 * Implemenation of the OS abstraction layer for the kernel device driver
16 #include <linux/spinlock.h>
17 #include <linux/rwsem.h>
18 #include <linux/mutex.h>
20 #include <linux/slab.h>
23 #include "mali_kernel_common.h"
25 /* These are all the locks we implement: */
28 _MALI_OSK_INTERNAL_LOCKTYPE_SPIN, /* Mutex, implicitly non-interruptable, use spin_lock/spin_unlock */
29 _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ, /* Mutex, IRQ version of spinlock, use spin_lock_irqsave/spin_unlock_irqrestore */
30 _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX, /* Interruptable, use mutex_unlock()/down_interruptable() */
31 _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT, /* Non-Interruptable, use mutex_unlock()/down() */
32 _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW, /* Non-interruptable, Reader/Writer, use {mutex_unlock,down}{read,write}() */
34 /* Linux supports, but we do not support:
35 * Non-Interruptable Reader/Writer spinlock mutexes - RW optimization will be switched off
38 /* Linux does not support:
39 * One-locks, of any sort - no optimization for this fact will be made.
42 } _mali_osk_internal_locktype;
44 struct _mali_osk_lock_t_struct
46 _mali_osk_internal_locktype type;
52 struct rw_semaphore rw_sema;
55 /** original flags for debug checking */
56 _mali_osk_lock_flags_t orig_flags;
58 /* id of the thread currently holding this lock, 0 if no
61 /* number of owners this lock currently has (can be > 1 if
62 * taken in R/O mode. */
64 /* what mode the lock was taken in */
65 _mali_osk_lock_mode_t mode;
66 ); /* MALI_DEBUG_CODE */
69 _mali_osk_lock_t *_mali_osk_lock_init( _mali_osk_lock_flags_t flags, u32 initial, u32 order )
71 _mali_osk_lock_t *lock = NULL;
73 /* Validate parameters: */
74 /* Flags acceptable */
75 MALI_DEBUG_ASSERT( 0 == ( flags & ~(_MALI_OSK_LOCKFLAG_SPINLOCK
76 | _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ
77 | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE
78 | _MALI_OSK_LOCKFLAG_READERWRITER
79 | _MALI_OSK_LOCKFLAG_ORDERED
80 | _MALI_OSK_LOCKFLAG_ONELOCK )) );
81 /* Spinlocks are always non-interruptable */
82 MALI_DEBUG_ASSERT( (((flags & _MALI_OSK_LOCKFLAG_SPINLOCK) || (flags & _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ)) && (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE))
83 || !(flags & _MALI_OSK_LOCKFLAG_SPINLOCK));
84 /* Parameter initial SBZ - for future expansion */
85 MALI_DEBUG_ASSERT( 0 == initial );
87 lock = kmalloc(sizeof(_mali_osk_lock_t), GFP_KERNEL);
94 /* Determine type of mutex: */
95 /* defaults to interruptable mutex if no flags are specified */
97 if ( (flags & _MALI_OSK_LOCKFLAG_SPINLOCK) )
99 /* Non-interruptable Spinlocks override all others */
100 lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_SPIN;
101 spin_lock_init( &lock->obj.spinlock );
103 else if ( (flags & _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ ) )
105 lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ;
107 spin_lock_init( &lock->obj.spinlock );
109 else if ( (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE)
110 && (flags & _MALI_OSK_LOCKFLAG_READERWRITER) )
112 lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW;
113 init_rwsem( &lock->obj.rw_sema );
117 /* Usual mutex types */
118 if ( (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE) )
120 lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT;
124 lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX;
127 /* Initially unlocked */
128 mutex_init(&lock->obj.mutex);
132 /* Debug tracking of flags */
133 lock->orig_flags = flags;
135 /* Debug tracking of lock owner */
144 u32 _mali_osk_lock_get_owner( _mali_osk_lock_t *lock )
149 u32 _mali_osk_lock_get_number_owners( _mali_osk_lock_t *lock )
151 return lock->nOwners;
154 u32 _mali_osk_lock_get_mode( _mali_osk_lock_t *lock )
160 _mali_osk_errcode_t _mali_osk_lock_wait( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode)
162 _mali_osk_errcode_t err = _MALI_OSK_ERR_OK;
164 /* Parameter validation */
165 MALI_DEBUG_ASSERT_POINTER( lock );
167 MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode
168 || _MALI_OSK_LOCKMODE_RO == mode );
170 /* Only allow RO locks when the initial object was a Reader/Writer lock
171 * Since information is lost on the internal locktype, we use the original
172 * information, which is only stored when built for DEBUG */
173 MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode
174 || (_MALI_OSK_LOCKMODE_RO == mode && (_MALI_OSK_LOCKFLAG_READERWRITER & lock->orig_flags)) );
176 switch ( lock->type )
178 case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN:
179 spin_lock(&lock->obj.spinlock);
181 case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ:
183 unsigned long tmp_flags;
184 spin_lock_irqsave(&lock->obj.spinlock, tmp_flags);
185 lock->flags = tmp_flags;
189 case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX:
190 if (mutex_lock_interruptible(&lock->obj.mutex))
192 MALI_PRINT_ERROR(("Can not lock mutex\n"));
193 err = _MALI_OSK_ERR_RESTARTSYSCALL;
197 case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT:
198 mutex_lock(&lock->obj.mutex);
201 case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW:
202 if (mode == _MALI_OSK_LOCKMODE_RO)
204 down_read(&lock->obj.rw_sema);
208 down_write(&lock->obj.rw_sema);
213 /* Reaching here indicates a programming error, so you will not get here
214 * on non-DEBUG builds */
215 MALI_DEBUG_PRINT_ERROR( ("Invalid internal lock type: %.8X", lock->type ) );
220 /* This thread is now the owner of this lock */
221 if (_MALI_OSK_ERR_OK == err)
223 if (mode == _MALI_OSK_LOCKMODE_RW)
225 /*MALI_DEBUG_ASSERT(0 == lock->owner);*/
226 if (0 != lock->owner)
228 printk(KERN_ERR "%d: ERROR: Lock %p already has owner %d\n", _mali_osk_get_tid(), lock, lock->owner);
231 lock->owner = _mali_osk_get_tid();
235 else /* mode == _MALI_OSK_LOCKMODE_RO */
237 lock->owner |= _mali_osk_get_tid();
247 void _mali_osk_lock_signal( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode )
249 /* Parameter validation */
250 MALI_DEBUG_ASSERT_POINTER( lock );
252 MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode
253 || _MALI_OSK_LOCKMODE_RO == mode );
255 /* Only allow RO locks when the initial object was a Reader/Writer lock
256 * Since information is lost on the internal locktype, we use the original
257 * information, which is only stored when built for DEBUG */
258 MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode
259 || (_MALI_OSK_LOCKMODE_RO == mode && (_MALI_OSK_LOCKFLAG_READERWRITER & lock->orig_flags)) );
262 /* make sure the thread releasing the lock actually was the owner */
263 if (mode == _MALI_OSK_LOCKMODE_RW)
265 /*MALI_DEBUG_ASSERT(_mali_osk_get_tid() == lock->owner);*/
266 if (_mali_osk_get_tid() != lock->owner)
268 printk(KERN_ERR "%d: ERROR: Lock %p owner was %d\n", _mali_osk_get_tid(), lock, lock->owner);
271 /* This lock now has no owner */
275 else /* mode == _MALI_OSK_LOCKMODE_RO */
277 if ((_mali_osk_get_tid() & lock->owner) != _mali_osk_get_tid())
279 printk(KERN_ERR "%d: ERROR: Not an owner of %p lock.\n", _mali_osk_get_tid(), lock);
283 /* if this is the last thread holding this lock in R/O mode, set owner
285 if (0 == --lock->nOwners)
292 switch ( lock->type )
294 case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN:
295 spin_unlock(&lock->obj.spinlock);
297 case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ:
298 spin_unlock_irqrestore(&lock->obj.spinlock, lock->flags);
301 case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX:
303 case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT:
304 mutex_unlock(&lock->obj.mutex);
307 case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW:
308 if (mode == _MALI_OSK_LOCKMODE_RO)
310 up_read(&lock->obj.rw_sema);
314 up_write(&lock->obj.rw_sema);
319 /* Reaching here indicates a programming error, so you will not get here
320 * on non-DEBUG builds */
321 MALI_DEBUG_PRINT_ERROR( ("Invalid internal lock type: %.8X", lock->type ) );
326 void _mali_osk_lock_term( _mali_osk_lock_t *lock )
328 /* Parameter validation */
329 MALI_DEBUG_ASSERT_POINTER( lock );
331 /* Linux requires no explicit termination of spinlocks, semaphores, or rw_semaphores */