msm: kgsl: improve active_cnt and ACTIVE state management

Require any code path which intends to touch the hardware
to take a reference on active_cnt with kgsl_active_count_get()
and release it with kgsl_active_count_put() when finished.
These functions now do the wake / sleep steps that were
previously handled by kgsl_check_suspended() and
kgsl_check_idle().

Additionally, kgsl_pre_hwaccess() will no longer turn on
the clocks, it just enforces via BUG_ON that the clocks
are enabled before a register is touched.

Change-Id: I31b0d067e6d600f0228450dbd73f69caa919ce13
Signed-off-by: Jeremy Gebben <jgebben@codeaurora.org>
This commit is contained in:
Jeremy Gebben
2013-04-11 16:30:05 -06:00
parent 46f755dac0
commit fcc201da78
7 changed files with 243 additions and 137 deletions
+13 -8
View File
@@ -2773,7 +2773,7 @@ static unsigned int adreno_isidle(struct kgsl_device *device)
if (device->state == KGSL_STATE_ACTIVE) {
/* Is the ring buffer is empty? */
GSL_RB_GET_READPTR(rb, &rb->rptr);
if (!device->active_cnt && (rb->rptr == rb->wptr)) {
if (rb->rptr == rb->wptr) {
/*
* Are there interrupts pending? If so then pretend we
* are not idle - this avoids the possiblity that we go
@@ -3591,15 +3591,20 @@ static void adreno_power_stats(struct kgsl_device *device,
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
unsigned int cycles;
unsigned int cycles = 0;
/* Get the busy cycles counted since the counter was last reset */
/* Calling this function also resets and restarts the counter */
/*
* Get the busy cycles counted since the counter was last reset.
* If we're not currently active, there shouldn't have been
* any cycles since the last time this function was called.
*/
if (device->state == KGSL_STATE_ACTIVE)
cycles = adreno_dev->gpudev->busy_cycles(adreno_dev);
cycles = adreno_dev->gpudev->busy_cycles(adreno_dev);
/* In order to calculate idle you have to have run the algorithm *
* at least once to get a start time. */
/*
* In order to calculate idle you have to have run the algorithm
* at least once to get a start time.
*/
if (pwr->time != 0) {
s64 tmp = ktime_to_us(ktime_get());
stats->total_time = tmp - pwr->time;
+3
View File
@@ -93,4 +93,7 @@ void adreno_debugfs_init(struct kgsl_device *device)
adreno_dev->ft_pf_policy = KGSL_FT_PAGEFAULT_DEFAULT_POLICY;
debugfs_create_u32("ft_pagefault_policy", 0644, device->d_debugfs,
&adreno_dev->ft_pf_policy);
debugfs_create_u32("active_cnt", 0444, device->d_debugfs,
&device->active_cnt);
}
+48 -52
View File
@@ -413,27 +413,6 @@ kgsl_context_destroy(struct kref *kref)
kfree(context);
}
static void kgsl_check_idle_locked(struct kgsl_device *device)
{
if (device->pwrctrl.nap_allowed == true &&
device->state == KGSL_STATE_ACTIVE &&
device->requested_state == KGSL_STATE_NONE) {
kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
kgsl_pwrscale_idle(device);
if (kgsl_pwrctrl_sleep(device) != 0)
mod_timer(&device->idle_timer,
jiffies +
device->pwrctrl.interval_timeout);
}
}
static void kgsl_check_idle(struct kgsl_device *device)
{
mutex_lock(&device->mutex);
kgsl_check_idle_locked(device);
mutex_unlock(&device->mutex);
}
struct kgsl_device *kgsl_get_device(int dev_idx)
{
int i;
@@ -496,13 +475,12 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state)
policy_saved = device->pwrscale.policy;
device->pwrscale.policy = NULL;
kgsl_pwrctrl_request_state(device, KGSL_STATE_SUSPEND);
/* Make sure no user process is waiting for a timestamp *
* before supending */
if (device->active_cnt != 0) {
mutex_unlock(&device->mutex);
wait_for_completion(&device->suspend_gate);
mutex_lock(&device->mutex);
}
/*
* Make sure no user process is waiting for a timestamp
* before supending.
*/
kgsl_active_count_wait(device);
/* Don't let the timer wake us during suspended sleep. */
del_timer_sync(&device->idle_timer);
switch (device->state) {
@@ -513,6 +491,8 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state)
device->ftbl->idle(device);
case KGSL_STATE_NAP:
case KGSL_STATE_SLEEP:
/* make sure power is on to stop the device */
kgsl_pwrctrl_enable(device);
/* Get the completion ready to be waited upon. */
INIT_COMPLETION(device->hwaccess_gate);
device->ftbl->suspend_context(device);
@@ -632,9 +612,16 @@ void kgsl_late_resume_driver(struct early_suspend *h)
device->pwrctrl.restore_slumber = false;
if (device->pwrscale.policy == NULL)
kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
kgsl_pwrctrl_wake(device);
if (kgsl_pwrctrl_wake(device) != 0)
return;
/*
* We don't have a way to go directly from
* a deeper sleep state to NAP, which is
* the desired state here.
*/
kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
kgsl_pwrctrl_sleep(device);
mutex_unlock(&device->mutex);
kgsl_check_idle(device);
KGSL_PWR_WARN(device, "late resume end\n");
}
EXPORT_SYMBOL(kgsl_late_resume_driver);
@@ -745,7 +732,7 @@ static int kgsl_release(struct inode *inodep, struct file *filep)
filep->private_data = NULL;
mutex_lock(&device->mutex);
kgsl_check_suspended(device);
kgsl_active_count_get(device);
while (1) {
context = idr_get_next(&device->context_idr, &next);
@@ -767,10 +754,17 @@ static int kgsl_release(struct inode *inodep, struct file *filep)
device->open_count--;
if (device->open_count == 0) {
BUG_ON(device->active_cnt > 1);
result = device->ftbl->stop(device);
kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
/*
* active_cnt special case: we just stopped the device,
* so no need to use kgsl_active_count_put()
*/
device->active_cnt--;
} else {
kgsl_active_count_put(device);
}
mutex_unlock(&device->mutex);
kfree(dev_priv);
@@ -816,9 +810,14 @@ static int kgsl_open(struct inode *inodep, struct file *filep)
filep->private_data = dev_priv;
mutex_lock(&device->mutex);
kgsl_check_suspended(device);
if (device->open_count == 0) {
/*
* active_cnt special case: we are starting up for the first
* time, so use this sequence instead of the kgsl_pwrctrl_wake()
* which will be called by kgsl_active_count_get().
*/
device->active_cnt++;
kgsl_sharedmem_set(&device->memstore, 0, 0,
device->memstore.size);
@@ -831,6 +830,7 @@ static int kgsl_open(struct inode *inodep, struct file *filep)
goto err_freedevpriv;
kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
kgsl_active_count_put(device);
}
device->open_count++;
mutex_unlock(&device->mutex);
@@ -856,10 +856,15 @@ err_stop:
mutex_lock(&device->mutex);
device->open_count--;
if (device->open_count == 0) {
/* make sure power is on to stop the device */
kgsl_pwrctrl_enable(device);
result = device->ftbl->stop(device);
kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
}
err_freedevpriv:
/* only the first open takes an active count */
if (device->open_count == 0)
device->active_cnt--;
mutex_unlock(&device->mutex);
filep->private_data = NULL;
kfree(dev_priv);
@@ -1073,10 +1078,6 @@ static long _device_waittimestamp(struct kgsl_device_private *dev_priv,
struct kgsl_device *device = dev_priv->device;
unsigned int context_id = context ? context->id : KGSL_MEMSTORE_GLOBAL;
/* Set the active count so that suspend doesn't do the wrong thing */
device->active_cnt++;
trace_kgsl_waittimestamp_entry(device, context_id,
kgsl_readtimestamp(device, context,
KGSL_TIMESTAMP_RETIRED),
@@ -1090,9 +1091,6 @@ static long _device_waittimestamp(struct kgsl_device_private *dev_priv,
KGSL_TIMESTAMP_RETIRED),
result);
/* Fire off any pending suspend operations that are in flight */
kgsl_active_count_put(dev_priv->device);
return result;
}
@@ -1887,7 +1885,6 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv,
trace_kgsl_mem_map(entry, param->fd);
kgsl_check_idle(dev_priv->device);
return result;
error_unmap:
@@ -1907,7 +1904,6 @@ error_put_file_ptr:
}
error:
kfree(entry);
kgsl_check_idle(dev_priv->device);
return result;
}
@@ -2035,7 +2031,6 @@ _gpumem_alloc(struct kgsl_device_private *dev_priv,
entry->memtype = KGSL_MEM_ENTRY_KERNEL;
kgsl_check_idle(dev_priv->device);
*ret_entry = entry;
return result;
err:
@@ -2370,7 +2365,7 @@ static const struct {
kgsl_ioctl_cff_user_event, 0),
KGSL_IOCTL_FUNC(IOCTL_KGSL_TIMESTAMP_EVENT,
kgsl_ioctl_timestamp_event,
KGSL_IOCTL_LOCK),
KGSL_IOCTL_LOCK | KGSL_IOCTL_WAKE),
KGSL_IOCTL_FUNC(IOCTL_KGSL_SETPROPERTY,
kgsl_ioctl_device_setproperty,
KGSL_IOCTL_LOCK | KGSL_IOCTL_WAKE),
@@ -2462,14 +2457,19 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
if (lock) {
mutex_lock(&dev_priv->device->mutex);
if (use_hw)
kgsl_check_suspended(dev_priv->device);
if (use_hw) {
ret = kgsl_active_count_get(dev_priv->device);
if (ret < 0)
goto unlock;
}
}
ret = func(dev_priv, cmd, uptr);
unlock:
if (lock) {
kgsl_check_idle_locked(dev_priv->device);
if (use_hw)
kgsl_active_count_put(dev_priv->device);
mutex_unlock(&dev_priv->device->mutex);
}
@@ -3032,11 +3032,7 @@ int kgsl_postmortem_dump(struct kgsl_device *device, int manual)
/* For a manual dump, make sure that the system is idle */
if (manual) {
if (device->active_cnt != 0) {
mutex_unlock(&device->mutex);
wait_for_completion(&device->suspend_gate);
mutex_lock(&device->mutex);
}
kgsl_active_count_wait(device);
if (device->state == KGSL_STATE_ACTIVE)
kgsl_idle(device);
-19
View File
@@ -454,23 +454,4 @@ kgsl_context_put(struct kgsl_context *context)
kref_put(&context->refcount, kgsl_context_destroy);
}
/**
* kgsl_active_count_put - Decrease the device active count
* @device: Pointer to a KGSL device
*
* Decrease the active count for the KGSL device and trigger the suspend_gate
* completion if it hits zero
*/
static inline void
kgsl_active_count_put(struct kgsl_device *device)
{
if (device->active_cnt == 1)
INIT_COMPLETION(device->suspend_gate);
device->active_cnt--;
if (device->active_cnt == 0)
complete(&device->suspend_gate);
}
#endif /* __KGSL_DEVICE_H */
+11 -7
View File
@@ -51,6 +51,7 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv,
void *owner)
{
int ret;
struct kgsl_event *event;
unsigned int cur_ts;
struct kgsl_context *context = NULL;
@@ -82,6 +83,16 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
if (event == NULL)
return -ENOMEM;
/*
* Increase the active count on the device to avoid going into power
* saving modes while events are pending
*/
ret = kgsl_active_count_get_light(device);
if (ret < 0) {
kfree(event);
return ret;
}
event->context = context;
event->timestamp = ts;
event->priv = priv;
@@ -112,13 +123,6 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
} else
_add_event_to_list(&device->events, event);
/*
* Increase the active count on the device to avoid going into power
* saving modes while events are pending
*/
device->active_cnt++;
queue_work(device->work_queue, &device->ts_expired_ws);
return 0;
}
+161 -49
View File
@@ -1055,6 +1055,14 @@ void kgsl_pwrctrl_close(struct kgsl_device *device)
pwr->power_flags = 0;
}
/**
* kgsl_idle_check() - Work function for GPU interrupts and idle timeouts.
* @device: The device
*
* This function is called for work that is queued by the interrupt
* handler or the idle timer. It attempts to transition to a clocks
* off state if the active_cnt is 0 and the hardware is idle.
*/
void kgsl_idle_check(struct work_struct *work)
{
struct kgsl_device *device = container_of(work, struct kgsl_device,
@@ -1064,15 +1072,22 @@ void kgsl_idle_check(struct work_struct *work)
return;
mutex_lock(&device->mutex);
if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) {
kgsl_pwrscale_idle(device);
if (kgsl_pwrctrl_sleep(device) != 0) {
kgsl_pwrscale_idle(device);
if (device->state == KGSL_STATE_ACTIVE
|| device->state == KGSL_STATE_NAP) {
if (device->active_cnt > 0 || kgsl_pwrctrl_sleep(device) != 0) {
kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
mod_timer(&device->idle_timer,
jiffies +
device->pwrctrl.interval_timeout);
/* If the GPU has been too busy to sleep, make sure *
* that is acurately reflected in the % busy numbers. */
/*
* If the GPU has been too busy to sleep, make sure
* that is acurately reflected in the % busy numbers.
*/
device->pwrctrl.clk_stats.no_nap_cnt++;
if (device->pwrctrl.clk_stats.no_nap_cnt >
UPDATE_BUSY) {
@@ -1104,54 +1119,26 @@ void kgsl_timer(unsigned long data)
}
}
/**
* kgsl_pre_hwaccess - Enforce preconditions for touching registers
* @device: The device
*
* This function ensures that the correct lock is held and that the GPU
* clock is on immediately before a register is read or written. Note
* that this function does not check active_cnt because the registers
* must be accessed during device start and stop, when the active_cnt
* may legitimately be 0.
*/
void kgsl_pre_hwaccess(struct kgsl_device *device)
{
/* In order to touch a register you must hold the device mutex...*/
BUG_ON(!mutex_is_locked(&device->mutex));
switch (device->state) {
case KGSL_STATE_ACTIVE:
return;
case KGSL_STATE_NAP:
case KGSL_STATE_SLEEP:
case KGSL_STATE_SLUMBER:
kgsl_pwrctrl_wake(device);
break;
case KGSL_STATE_SUSPEND:
kgsl_check_suspended(device);
break;
case KGSL_STATE_INIT:
case KGSL_STATE_HUNG:
case KGSL_STATE_DUMP_AND_FT:
if (test_bit(KGSL_PWRFLAGS_CLK_ON,
&device->pwrctrl.power_flags))
break;
else
KGSL_PWR_ERR(device,
"hw access while clocks off from state %d\n",
device->state);
break;
default:
KGSL_PWR_ERR(device, "hw access while in unknown state %d\n",
device->state);
break;
}
/* and have the clock on! */
BUG_ON(!test_bit(KGSL_PWRFLAGS_CLK_ON, &device->pwrctrl.power_flags));
}
EXPORT_SYMBOL(kgsl_pre_hwaccess);
void kgsl_check_suspended(struct kgsl_device *device)
{
if (device->requested_state == KGSL_STATE_SUSPEND ||
device->state == KGSL_STATE_SUSPEND) {
mutex_unlock(&device->mutex);
wait_for_completion(&device->hwaccess_gate);
mutex_lock(&device->mutex);
} else if (device->state == KGSL_STATE_DUMP_AND_FT) {
mutex_unlock(&device->mutex);
wait_for_completion(&device->ft_gate);
mutex_lock(&device->mutex);
} else if (device->state == KGSL_STATE_SLUMBER)
kgsl_pwrctrl_wake(device);
}
static int
_nap(struct kgsl_device *device)
{
@@ -1230,6 +1217,8 @@ _slumber(struct kgsl_device *device)
case KGSL_STATE_NAP:
case KGSL_STATE_SLEEP:
del_timer_sync(&device->idle_timer);
/* make sure power is on to stop the device*/
kgsl_pwrctrl_enable(device);
device->ftbl->suspend_context(device);
device->ftbl->stop(device);
_sleep_accounting(device);
@@ -1278,9 +1267,9 @@ EXPORT_SYMBOL(kgsl_pwrctrl_sleep);
/******************************************************************/
/* Caller must hold the device mutex. */
void kgsl_pwrctrl_wake(struct kgsl_device *device)
int kgsl_pwrctrl_wake(struct kgsl_device *device)
{
int status;
int status = 0;
unsigned int context_id;
unsigned int state = device->state;
unsigned int ts_processed = 0xdeaddead;
@@ -1329,8 +1318,10 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device)
KGSL_PWR_WARN(device, "unhandled state %s\n",
kgsl_pwrstate_to_str(device->state));
kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
status = -EINVAL;
break;
}
return status;
}
EXPORT_SYMBOL(kgsl_pwrctrl_wake);
@@ -1396,3 +1387,124 @@ const char *kgsl_pwrstate_to_str(unsigned int state)
}
EXPORT_SYMBOL(kgsl_pwrstate_to_str);
/**
* kgsl_active_count_get() - Increase the device active count
* @device: Pointer to a KGSL device
*
* Increase the active count for the KGSL device and turn on
* clocks if this is the first reference. Code paths that need
* to touch the hardware or wait for the hardware to complete
* an operation must hold an active count reference until they
* are finished. An error code will be returned if waking the
* device fails. The device mutex must be held while *calling
* this function.
*/
int kgsl_active_count_get(struct kgsl_device *device)
{
int ret = 0;
BUG_ON(!mutex_is_locked(&device->mutex));
if (device->active_cnt == 0) {
if (device->requested_state == KGSL_STATE_SUSPEND ||
device->state == KGSL_STATE_SUSPEND) {
mutex_unlock(&device->mutex);
wait_for_completion(&device->hwaccess_gate);
mutex_lock(&device->mutex);
} else if (device->state == KGSL_STATE_DUMP_AND_FT) {
mutex_unlock(&device->mutex);
wait_for_completion(&device->ft_gate);
mutex_lock(&device->mutex);
}
ret = kgsl_pwrctrl_wake(device);
}
if (ret == 0)
device->active_cnt++;
return ret;
}
EXPORT_SYMBOL(kgsl_active_count_get);
/**
* kgsl_active_count_get_light() - Increase the device active count
* @device: Pointer to a KGSL device
*
* Increase the active count for the KGSL device WITHOUT
* turning on the clocks. Currently this is only used for creating
* kgsl_events. The device mutex must be held while calling this function.
*/
int kgsl_active_count_get_light(struct kgsl_device *device)
{
BUG_ON(!mutex_is_locked(&device->mutex));
if (device->state != KGSL_STATE_ACTIVE) {
dev_WARN_ONCE(device->dev, 1, "device in unexpected state %s\n",
kgsl_pwrstate_to_str(device->state));
return -EINVAL;
}
if (device->active_cnt == 0) {
dev_WARN_ONCE(device->dev, 1, "active count is 0!\n");
return -EINVAL;
}
device->active_cnt++;
return 0;
}
EXPORT_SYMBOL(kgsl_active_count_get_light);
/**
* kgsl_active_count_put() - Decrease the device active count
* @device: Pointer to a KGSL device
*
* Decrease the active count for the KGSL device and turn off
* clocks if there are no remaining references. This function will
* transition the device to NAP if there are no other pending state
* changes. It also completes the suspend gate. The device mutex must
* be held while calling this function.
*/
void kgsl_active_count_put(struct kgsl_device *device)
{
BUG_ON(!mutex_is_locked(&device->mutex));
BUG_ON(device->active_cnt == 0);
kgsl_pwrscale_idle(device);
if (device->active_cnt > 1) {
device->active_cnt--;
return;
}
INIT_COMPLETION(device->suspend_gate);
if (device->pwrctrl.nap_allowed == true &&
(device->state == KGSL_STATE_ACTIVE &&
device->requested_state == KGSL_STATE_NONE)) {
kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
if (kgsl_pwrctrl_sleep(device) != 0)
mod_timer(&device->idle_timer,
jiffies
+ device->pwrctrl.interval_timeout);
}
device->active_cnt--;
if (device->active_cnt == 0)
complete(&device->suspend_gate);
}
EXPORT_SYMBOL(kgsl_active_count_put);
/**
* kgsl_active_count_wait() - Wait for activity to finish.
* @device: Pointer to a KGSL device
*
* Block until all active_cnt users put() their reference.
*/
void kgsl_active_count_wait(struct kgsl_device *device)
{
BUG_ON(!mutex_is_locked(&device->mutex));
if (device->active_cnt != 0) {
mutex_unlock(&device->mutex);
wait_for_completion(&device->suspend_gate);
mutex_lock(&device->mutex);
}
}
EXPORT_SYMBOL(kgsl_active_count_wait);
+7 -2
View File
@@ -99,9 +99,8 @@ void kgsl_pwrctrl_close(struct kgsl_device *device);
void kgsl_timer(unsigned long data);
void kgsl_idle_check(struct work_struct *work);
void kgsl_pre_hwaccess(struct kgsl_device *device);
void kgsl_check_suspended(struct kgsl_device *device);
int kgsl_pwrctrl_sleep(struct kgsl_device *device);
void kgsl_pwrctrl_wake(struct kgsl_device *device);
int kgsl_pwrctrl_wake(struct kgsl_device *device);
void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
unsigned int level);
int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device);
@@ -115,4 +114,10 @@ static inline unsigned long kgsl_get_clkrate(struct clk *clk)
void kgsl_pwrctrl_set_state(struct kgsl_device *device, unsigned int state);
void kgsl_pwrctrl_request_state(struct kgsl_device *device, unsigned int state);
int kgsl_active_count_get(struct kgsl_device *device);
int kgsl_active_count_get_light(struct kgsl_device *device);
void kgsl_active_count_put(struct kgsl_device *device);
void kgsl_active_count_wait(struct kgsl_device *device);
#endif /* __KGSL_PWRCTRL_H */