Skip to content

Commit

Permalink
hwspinlock: Introduce one new mode for hwspinlock
Browse files Browse the repository at this point in the history
In some scenarios, user need do some time-consuming or sleepable
operations under the hardware spinlock protection for synchronization
between the multiple subsystems.

For example, there is one PMIC efuse on Spreadtrum platform, which
need to be accessed under one hardware lock. But during the hardware
lock protection, the efuse operation is time-consuming to almost 5 ms,
so we can not disable the interrupts or preemption so long in this case.

Thus we can introduce one new mode to indicate that we just acquire the
hardware lock and do not disable interrupts or preemption, meanwhile we
should force user to protect the hardware lock with mutex or spinlock to
avoid dead-lock.

Signed-off-by: Baolin Wang <[email protected]>
Signed-off-by: Bjorn Andersson <[email protected]>
  • Loading branch information
wangbaolin719 authored and andersson committed Apr 17, 2018
1 parent 66742b1 commit 1e6c06a
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 7 deletions.
34 changes: 27 additions & 7 deletions drivers/hwspinlock/hwspinlock_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,16 @@ static DEFINE_MUTEX(hwspinlock_tree_lock);
* This function attempts to lock an hwspinlock, and will immediately
* fail if the hwspinlock is already taken.
*
* Upon a successful return from this function, preemption (and possibly
* interrupts) is disabled, so the caller must not sleep, and is advised to
* release the hwspinlock as soon as possible. This is required in order to
* minimize remote cores polling on the hardware interconnect.
* Caution: If the mode is HWLOCK_RAW, that means user must protect the routine
* of getting hardware lock with mutex or spinlock. Since in some scenarios,
* user need some time-consuming or sleepable operations under the hardware
* lock, they need one sleepable lock (like mutex) to protect the operations.
*
* If the mode is not HWLOCK_RAW, upon a successful return from this function,
* preemption (and possibly interrupts) is disabled, so the caller must not
* sleep, and is advised to release the hwspinlock as soon as possible. This is
* required in order to minimize remote cores polling on the hardware
* interconnect.
*
* The user decides whether local interrupts are disabled or not, and if yes,
* whether he wants their previous state to be saved. It is up to the user
Expand Down Expand Up @@ -113,6 +119,9 @@ int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
case HWLOCK_IRQ:
ret = spin_trylock_irq(&hwlock->lock);
break;
case HWLOCK_RAW:
ret = 1;
break;
default:
ret = spin_trylock(&hwlock->lock);
break;
Expand All @@ -134,6 +143,9 @@ int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
case HWLOCK_IRQ:
spin_unlock_irq(&hwlock->lock);
break;
case HWLOCK_RAW:
/* Nothing to do */
break;
default:
spin_unlock(&hwlock->lock);
break;
Expand Down Expand Up @@ -170,9 +182,14 @@ EXPORT_SYMBOL_GPL(__hwspin_trylock);
* is already taken, the function will busy loop waiting for it to
* be released, but give up after @timeout msecs have elapsed.
*
* Upon a successful return from this function, preemption is disabled
* (and possibly local interrupts, too), so the caller must not sleep,
* and is advised to release the hwspinlock as soon as possible.
* Caution: If the mode is HWLOCK_RAW, that means user must protect the routine
* of getting hardware lock with mutex or spinlock. Since in some scenarios,
* user need some time-consuming or sleepable operations under the hardware
* lock, they need one sleepable lock (like mutex) to protect the operations.
*
* If the mode is not HWLOCK_RAW, upon a successful return from this function,
* preemption is disabled (and possibly local interrupts, too), so the caller
* must not sleep, and is advised to release the hwspinlock as soon as possible.
* This is required in order to minimize remote cores polling on the
* hardware interconnect.
*
Expand Down Expand Up @@ -266,6 +283,9 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
case HWLOCK_IRQ:
spin_unlock_irq(&hwlock->lock);
break;
case HWLOCK_RAW:
/* Nothing to do */
break;
default:
spin_unlock(&hwlock->lock);
break;
Expand Down
58 changes: 58 additions & 0 deletions include/linux/hwspinlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
/* hwspinlock mode argument */
#define HWLOCK_IRQSTATE 0x01 /* Disable interrupts, save state */
#define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */
#define HWLOCK_RAW 0x03

struct device;
struct device_node;
Expand Down Expand Up @@ -175,6 +176,25 @@ static inline int hwspin_trylock_irq(struct hwspinlock *hwlock)
return __hwspin_trylock(hwlock, HWLOCK_IRQ, NULL);
}

/**
* hwspin_trylock_raw() - attempt to lock a specific hwspinlock
* @hwlock: an hwspinlock which we want to trylock
*
* This function attempts to lock an hwspinlock, and will immediately fail
* if the hwspinlock is already taken.
*
* Caution: User must protect the routine of getting hardware lock with mutex
* or spinlock to avoid dead-lock, that will let user can do some time-consuming
* or sleepable operations under the hardware lock.
*
* Returns 0 if we successfully locked the hwspinlock, -EBUSY if
* the hwspinlock was already taken, and -EINVAL if @hwlock is invalid.
*/
static inline int hwspin_trylock_raw(struct hwspinlock *hwlock)
{
return __hwspin_trylock(hwlock, HWLOCK_RAW, NULL);
}

/**
* hwspin_trylock() - attempt to lock a specific hwspinlock
* @hwlock: an hwspinlock which we want to trylock
Expand Down Expand Up @@ -242,6 +262,29 @@ int hwspin_lock_timeout_irq(struct hwspinlock *hwlock, unsigned int to)
return __hwspin_lock_timeout(hwlock, to, HWLOCK_IRQ, NULL);
}

/**
* hwspin_lock_timeout_raw() - lock an hwspinlock with timeout limit
* @hwlock: the hwspinlock to be locked
* @to: timeout value in msecs
*
* This function locks the underlying @hwlock. If the @hwlock
* is already taken, the function will busy loop waiting for it to
* be released, but give up when @timeout msecs have elapsed.
*
* Caution: User must protect the routine of getting hardware lock with mutex
* or spinlock to avoid dead-lock, that will let user can do some time-consuming
* or sleepable operations under the hardware lock.
*
* Returns 0 when the @hwlock was successfully taken, and an appropriate
* error code otherwise (most notably an -ETIMEDOUT if the @hwlock is still
* busy after @timeout msecs). The function will never sleep.
*/
static inline
int hwspin_lock_timeout_raw(struct hwspinlock *hwlock, unsigned int to)
{
return __hwspin_lock_timeout(hwlock, to, HWLOCK_RAW, NULL);
}

/**
* hwspin_lock_timeout() - lock an hwspinlock with timeout limit
* @hwlock: the hwspinlock to be locked
Expand Down Expand Up @@ -301,6 +344,21 @@ static inline void hwspin_unlock_irq(struct hwspinlock *hwlock)
__hwspin_unlock(hwlock, HWLOCK_IRQ, NULL);
}

/**
* hwspin_unlock_raw() - unlock hwspinlock
* @hwlock: a previously-acquired hwspinlock which we want to unlock
*
* This function will unlock a specific hwspinlock.
*
* @hwlock must be already locked (e.g. by hwspin_trylock()) before calling
* this function: it is a bug to call unlock on a @hwlock that is already
* unlocked.
*/
static inline void hwspin_unlock_raw(struct hwspinlock *hwlock)
{
__hwspin_unlock(hwlock, HWLOCK_RAW, NULL);
}

/**
* hwspin_unlock() - unlock hwspinlock
* @hwlock: a previously-acquired hwspinlock which we want to unlock
Expand Down

0 comments on commit 1e6c06a

Please sign in to comment.