diff options
author | Jack Diver <diverj@google.com> | 2023-11-08 17:09:20 +0000 |
---|---|---|
committer | Jack Diver <diverj@google.com> | 2024-02-28 18:24:08 +0000 |
commit | ee7d8af92a5ddd8645dc4ec689e3eab8a461bf53 (patch) | |
tree | c2629e349473118d15b8eabdb1c071c9fb274181 /mali_pixel | |
parent | 2dd21a70c75d09b6e73838f8882e19dd734a66f0 (diff) | |
download | gpu-ee7d8af92a5ddd8645dc4ec689e3eab8a461bf53.tar.gz |
mali_pixel: Implement SLC partition ref counting
Bug: 313458962
Test: gfxbench
Signed-off-by: Jack Diver <diverj@google.com>
(cherry picked from https://partner-android-review.googlesource.com/q/commit:ef62a44e1e80286433fae8918996eb57647ba84c)
Merged-In: Id2ab03db4b8b3122bc1f87ecb97715fa3a5d80e4
Change-Id: Id2ab03db4b8b3122bc1f87ecb97715fa3a5d80e4
Diffstat (limited to 'mali_pixel')
-rw-r--r-- | mali_pixel/memory_group_manager.c | 16 | ||||
-rw-r--r-- | mali_pixel/pixel_slc.c | 129 | ||||
-rw-r--r-- | mali_pixel/pixel_slc.h | 13 |
3 files changed, 158 insertions, 0 deletions
diff --git a/mali_pixel/memory_group_manager.c b/mali_pixel/memory_group_manager.c index 4d92ea7..03c6f74 100644 --- a/mali_pixel/memory_group_manager.c +++ b/mali_pixel/memory_group_manager.c @@ -533,6 +533,22 @@ static vm_fault_t mgm_vmf_insert_pfn_prot( return fault; } +void pixel_mgm_slc_inc_refcount(struct memory_group_manager_device* mgm_dev) +{ + struct mgm_groups *const data = mgm_dev->data; + + slc_inc_refcount(&data->slc_data); +} +EXPORT_SYMBOL_GPL(pixel_mgm_slc_inc_refcount); + +void pixel_mgm_slc_dec_refcount(struct memory_group_manager_device* mgm_dev) +{ + struct mgm_groups *const data = mgm_dev->data; + + slc_dec_refcount(&data->slc_data); +} +EXPORT_SYMBOL_GPL(pixel_mgm_slc_dec_refcount); + static int mgm_initialize_data(struct mgm_groups *mgm_data) { int i, ret; diff --git a/mali_pixel/pixel_slc.c b/mali_pixel/pixel_slc.c index 62c6908..78f1b74 100644 --- a/mali_pixel/pixel_slc.c +++ b/mali_pixel/pixel_slc.c @@ -37,6 +37,50 @@ #endif #define PBHA_BIT_MASK (0xf) +#define PARTITION_DISABLE_HYSTERESIS (msecs_to_jiffies(100)) + + +/** + * partition_required() - Determine whether we require a partition to be enabled + * + * @pt: The partition to check. + * + * Check whether a partition meets the requirements for being enabled. + * + * Return: True, if the partition is required to be enabled, otherwise false. + */ +static bool partition_required(struct slc_partition *pt) +{ + lockdep_assert_held(&pt->lock); + + return atomic_read(&pt->refcount); +} + +/** + * pixel_atomic_dec_and_lock_irqsave - lock on reaching reference count zero + * + * @val: The atomic counter + * @lock: The spinlock in question + * @flags: Storage for the current interrupt enable state + * + * Decrements @val by 1, if the result is 0, locks @lock. + * + * Return: True if the lock was taken, false for all other cases. + */ +static int pixel_atomic_dec_and_lock_irqsave(atomic_t* val, spinlock_t* lock, unsigned long* flags) +{ + /* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */ + if (atomic_add_unless(val, -1, 1)) + return 0; + + /* Otherwise do it the slow way */ + spin_lock_irqsave(lock, *flags); + if (atomic_dec_and_test(val)) + return 1; + spin_unlock_irqrestore(lock, *flags); + + return 0; +} /** * slc_wipe_pbha - Clear any set PBHA bits from the pte. @@ -104,6 +148,84 @@ static void disable_partition(struct slc_data *data, struct slc_partition *pt) } /** + * queue_disable_worker - Queue a delayed partition disable op + * + * @data: The &struct slc_data tracking partition information. + */ +static void queue_disable_worker(struct slc_data *data) +{ + queue_delayed_work(system_highpri_wq, &data->disable_work, PARTITION_DISABLE_HYSTERESIS); +} + +/** + * partition_disable_worker - Callback to lazily disable a partition + * + * @work: The &struct work_struct dequeued + */ +static void partition_disable_worker(struct work_struct *work) +{ + struct slc_data* data = container_of(work, struct slc_data, disable_work.work); + struct slc_partition *pt = &data->partition; + unsigned long flags; + + /* Complete any pending disable ops */ + spin_lock_irqsave(&pt->lock, flags); + + if (!partition_required(pt)) + disable_partition(data, pt); + + spin_unlock_irqrestore(&pt->lock, flags); +} + +/** + * slc_inc_refcount - Increase the partition reference count. + * + * @data: The &struct slc_data tracking partition information. + * + * If this is the first reference being taken, the partition will be enabled. + */ +void slc_inc_refcount(struct slc_data *data) +{ + struct slc_partition *pt = &data->partition; + + /* Try to re-enable the partition if this is the first reference */ + if (atomic_inc_return(&pt->refcount) == 1) { + unsigned long flags; + + spin_lock_irqsave(&pt->lock, flags); + + /* Enable the partition immediately if it's required */ + if (partition_required(pt)) + enable_partition(data, pt); + + spin_unlock_irqrestore(&pt->lock, flags); + } +} + +/** + * slc_dec_refcount - Decrease the partition reference count. + * + * @data: The &struct slc_data tracking partition information. + * + * If this is the last reference being released, the partition will be disabled. + */ +void slc_dec_refcount(struct slc_data *data) +{ + struct slc_partition *pt = &data->partition; + unsigned long flags; + + /* Disable the partition if this was the last reference */ + if (pixel_atomic_dec_and_lock_irqsave(&pt->refcount, &pt->lock, &flags)) { + + /* Lazily disable the partition if it's no longer required */ + if (!partition_required(pt)) + queue_disable_worker(data); + + spin_unlock_irqrestore(&pt->lock, flags); + } +} + +/** * init_partition - Register and initialize a partition with the SLC driver. * * @data: The &struct slc_data tracking partition information. @@ -141,7 +263,9 @@ static int init_partition(struct slc_data *data, struct slc_partition *pt, u32 i .ptid = ptid, .pbha = pbha, .enabled = false, + .refcount = ATOMIC_INIT(0), }; + spin_lock_init(&pt->lock); err_exit: return err; @@ -180,6 +304,8 @@ int slc_init_data(struct slc_data *data, struct device* dev) /* Inherit the platform device */ data->dev = dev; + INIT_DELAYED_WORK(&data->disable_work, partition_disable_worker); + /* Register our node with the SLC driver. * This detects our partitions defined within the DT. */ @@ -209,6 +335,9 @@ err_exit: */ void slc_term_data(struct slc_data *data) { + /* Ensure all pending disable ops are complete */ + cancel_delayed_work_sync(&data->disable_work); + term_partition(data, &data->partition); pt_client_unregister(data->pt_handle); diff --git a/mali_pixel/pixel_slc.h b/mali_pixel/pixel_slc.h index 40b5ad7..bcaf1ff 100644 --- a/mali_pixel/pixel_slc.h +++ b/mali_pixel/pixel_slc.h @@ -45,6 +45,12 @@ struct slc_partition { /** @enabled: Is the partition currently enabled */ bool enabled; + + /** @refcount: Reference count for this partition */ + atomic_t refcount; + + /** @lock: Lock protecting enable/disable ops on this partition */ + spinlock_t lock; }; /** @@ -59,6 +65,9 @@ struct slc_data { /** @dev: Inherited pointer to device attached */ struct device *dev; + + /** @disable_work: Work item used to queue lazy SLC partition disable ops. */ + struct delayed_work disable_work; }; int slc_init_data(struct slc_data *data, struct device* dev); @@ -69,4 +78,8 @@ u64 slc_set_pbha(struct slc_data const *data, u64 pte); u64 slc_wipe_pbha(u64 pte); +void slc_inc_refcount(struct slc_data *data); + +void slc_dec_refcount(struct slc_data *data); + #endif /* _PIXEL_SLC_H_ */ |