Skip to content

Commit f32f6db

Browse files
Aakash-Sarkarceladon
authored andcommitted
Add GPU work period support for Xe driver
This patch implements the Android VSR requirement GPU work period event for Intel GPUs The requirement is implemented using a timer to give periodic interrupts and a worker thread per app instance to accumulate its run time on gpu in nanoseconds using the hw ctx timestamp and emit the event. Tracked-On: ALOS-1015 Signed-off-by: Aakash Sarkar <aakash.deep.sarkar@intel.com>
1 parent afd36ec commit f32f6db

8 files changed

Lines changed: 348 additions & 1 deletion

File tree

drivers/gpu/drm/xe/xe_device.c

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/aperture.h>
99
#include <linux/delay.h>
1010
#include <linux/fault-inject.h>
11+
#include <linux/jiffies.h>
1112
#include <linux/units.h>
1213

1314
#include <drm/drm_atomic_helper.h>
@@ -65,9 +66,13 @@ static int xe_file_open(struct drm_device *dev, struct drm_file *file)
6566
{
6667
struct xe_device *xe = to_xe_device(dev);
6768
struct xe_drm_client *client;
69+
struct xe_user *user;
6870
struct xe_file *xef;
6971
int ret = -ENOMEM;
72+
unsigned long flags;
73+
unsigned int uid = 0;
7074
struct task_struct *task = NULL;
75+
const struct cred *cred = NULL;
7176

7277
xef = kzalloc(sizeof(*xef), GFP_KERNEL);
7378
if (!xef)
@@ -94,11 +99,52 @@ static int xe_file_open(struct drm_device *dev, struct drm_file *file)
9499

95100
task = get_pid_task(rcu_access_pointer(file->pid), PIDTYPE_PID);
96101
if (task) {
102+
cred = get_task_cred(task);
103+
if (cred) {
104+
uid = cred->euid.val;
105+
put_cred(cred);
106+
}
97107
xef->process_name = kstrdup(task->comm, GFP_KERNEL);
98108
xef->pid = task->pid;
99109
put_task_struct(task);
100110
}
101111

112+
/*
113+
* Check if the calling process/uid has already been registered
114+
* by the xe driver during a previous file open call. If so then
115+
* take a reference to this xe file and add it to the list of xe
116+
* files belonging to the this process' xe user
117+
*/
118+
spin_lock_irqsave(&xe->work_period.lock, flags);
119+
list_for_each_entry(user, &xe->work_period.user_list, entry) {
120+
if (user->uid == uid) {
121+
xef->user = xe_user_get(user);
122+
goto filelist_add;
123+
}
124+
}
125+
/* list_add could be ordered wrt to list_for_each_entry */
126+
smp_mb();
127+
128+
/*
129+
* We couldn't find a xe user for this process. allocate a new
130+
* xe user structure and register it to the xe driver
131+
*/
132+
user = xe_user_alloc(uid);
133+
if (!user) {
134+
goto spin_unlk;
135+
}
136+
137+
user->last_timestamp_ns = ktime_get_raw_ns();
138+
list_add(&user->entry, &xe->work_period.user_list);
139+
xef->user = user;
140+
141+
filelist_add:
142+
spin_lock(&user->filelist_lock);
143+
list_add(&xef->user_link, &user->filelist);
144+
spin_unlock(&user->filelist_lock);
145+
user->xe = xe;
146+
spin_unlk:
147+
spin_unlock_irqrestore(&xe->work_period.lock, flags);
102148
return 0;
103149
}
104150

@@ -113,6 +159,9 @@ static void xe_file_destroy(struct kref *ref)
113159

114160
xe_drm_client_put(xef->client);
115161
kfree(xef->process_name);
162+
163+
list_del(&xef->user_link);
164+
xe_user_put(xef->user);
116165
kfree(xef);
117166
}
118167

@@ -231,6 +280,24 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
231280
#define xe_drm_compat_ioctl NULL
232281
#endif
233282

283+
static void work_period_timer_fn(struct timer_list *timer)
284+
{
285+
struct xe_device *xe = container_of(timer, typeof(*xe), work_period.timer);
286+
struct xe_user *user;
287+
unsigned long timeout = 0;
288+
289+
spin_lock(&xe->work_period.lock);
290+
if (!list_empty(&xe->work_period.user_list)) {
291+
list_for_each_entry(user, &xe->work_period.user_list, entry) {
292+
queue_work(xe->work_period.wq, &user->work);
293+
}
294+
}
295+
spin_unlock(&xe->work_period.lock);
296+
timeout = jiffies + msecs_to_jiffies(500);
297+
298+
mod_timer(timer, timeout);
299+
}
300+
234301
static const struct file_operations xe_driver_fops = {
235302
.owner = THIS_MODULE,
236303
.open = drm_open,
@@ -291,6 +358,11 @@ static void xe_device_destroy(struct drm_device *dev, void *dummy)
291358
if (xe->destroy_wq)
292359
destroy_workqueue(xe->destroy_wq);
293360

361+
if (xe->work_period.wq)
362+
destroy_workqueue(xe->work_period.wq);
363+
364+
del_timer(&xe->work_period.timer);
365+
294366
ttm_device_fini(&xe->ttm);
295367
}
296368

@@ -350,13 +422,22 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
350422
INIT_LIST_HEAD(&xe->pinned.external_vram);
351423
INIT_LIST_HEAD(&xe->pinned.evicted);
352424

425+
spin_lock_init(&xe->work_period.lock);
426+
INIT_LIST_HEAD(&xe->work_period.user_list);
427+
timer_setup(&xe->work_period.timer, work_period_timer_fn, 0);
428+
xe->work_period.timer.expires = jiffies + msecs_to_jiffies(1000);
429+
add_timer(&xe->work_period.timer);
430+
431+
xe->work_period.wq = alloc_workqueue("xe-work-period-wq", 0, 0);
432+
353433
xe->preempt_fence_wq = alloc_ordered_workqueue("xe-preempt-fence-wq",
354434
WQ_MEM_RECLAIM);
355435
xe->ordered_wq = alloc_ordered_workqueue("xe-ordered-wq", 0);
356436
xe->unordered_wq = alloc_workqueue("xe-unordered-wq", 0, 0);
357437
xe->destroy_wq = alloc_workqueue("xe-destroy-wq", 0, 0);
358438
if (!xe->ordered_wq || !xe->unordered_wq ||
359-
!xe->preempt_fence_wq || !xe->destroy_wq) {
439+
!xe->preempt_fence_wq || !xe->destroy_wq ||
440+
!xe->work_period.wq) {
360441
/*
361442
* Cleanup done in xe_device_destroy via
362443
* drmm_add_action_or_reset register above

drivers/gpu/drm/xe/xe_device_types.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,20 @@ struct xe_device {
405405
struct list_head external_vram;
406406
} pinned;
407407

408+
/** @work_period: gpu work period event */
409+
struct {
410+
/** @lock: lock protecting this structure */
411+
spinlock_t lock;
412+
/** @timer: timer to give periodic interrupts to emit the
413+
* gpu work period event
414+
*/
415+
struct timer_list timer;
416+
/** @user_list: list of xe users using this xe device */
417+
struct list_head user_list;
418+
/** @wq: workqueue for work period event emitting work */
419+
struct workqueue_struct *wq;
420+
} work_period;
421+
408422
/** @ufence_wq: user fence wait queue */
409423
wait_queue_head_t ufence_wq;
410424

@@ -619,6 +633,9 @@ struct xe_file {
619633
/** @run_ticks: hw engine class run time in ticks for this drm client */
620634
u64 run_ticks[XE_ENGINE_CLASS_MAX];
621635

636+
/** @active_duration_ns: total run time in ns for this drm client */
637+
u64 active_duration_ns;
638+
622639
/** @client: drm client */
623640
struct xe_drm_client *client;
624641

@@ -634,6 +651,15 @@ struct xe_file {
634651
*/
635652
pid_t pid;
636653

654+
/**
655+
* @user_link: entry into xe_user.filelist list
656+
*/
657+
struct list_head user_link;
658+
/**
659+
* @user: pointer to the xe user struct that opened this xe file
660+
*/
661+
struct xe_user *user;
662+
637663
/** @refcount: ref count of this xe file */
638664
struct kref refcount;
639665
};

drivers/gpu/drm/xe/xe_drm_client.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#include "xe_pm.h"
2222
#include "xe_trace.h"
2323

24+
#define CREATE_TRACE_POINTS
25+
#include "xe_power_gpu_work_period_trace.h"
26+
2427
/**
2528
* DOC: DRM Client usage stats
2629
*
@@ -395,3 +398,109 @@ void xe_drm_client_fdinfo(struct drm_printer *p, struct drm_file *file)
395398
show_run_ticks(p, file);
396399
}
397400
#endif
401+
402+
/**
403+
* worker thread to emit gpu work period event for this xe user
404+
* @work: work instance for this xe user
405+
*
406+
* Return: void
407+
*/
408+
static inline void work_period_worker(struct work_struct *work)
409+
{
410+
struct xe_user *user = container_of(work, struct xe_user, work);
411+
struct xe_device *xe = user->xe;
412+
struct xe_file *xef;
413+
struct xe_exec_queue *q;
414+
u64 last_active_duration, last_timestamp;
415+
u32 gpuid = 0, uid = user->uid;
416+
u64 start_time, end_time, active_duration;
417+
unsigned long i;
418+
419+
last_active_duration = user->active_duration_ns;
420+
last_timestamp = user->last_timestamp_ns;
421+
422+
xe_pm_runtime_get(xe);
423+
424+
if (!list_empty(&user->filelist)) {
425+
spin_lock(&user->filelist_lock);
426+
list_for_each_entry(xef, &user->filelist, user_link) {
427+
428+
/*
429+
* Wait for any exec queue going away:
430+
* their cycles will get updated on context switch out,
431+
* so wait for that to happen
432+
*/
433+
wait_var_event(&xef->exec_queue.pending_removal,
434+
!atomic_read(&xef->exec_queue.pending_removal));
435+
436+
/* Accumulate all the exec queues from this xe file */
437+
mutex_lock(&xef->exec_queue.lock);
438+
xa_for_each(&xef->exec_queue.xa, i, q) {
439+
xe_exec_queue_get(q);
440+
mutex_unlock(&xef->exec_queue.lock);
441+
442+
xe_exec_queue_update_run_ticks(q);
443+
444+
mutex_lock(&xef->exec_queue.lock);
445+
xe_exec_queue_put(q);
446+
}
447+
mutex_unlock(&xef->exec_queue.lock);
448+
user->active_duration_ns += xef->active_duration_ns;
449+
}
450+
spin_unlock(&user->filelist_lock);
451+
}
452+
453+
xe_pm_runtime_put(xe);
454+
455+
start_time = last_timestamp + 1;
456+
end_time = ktime_get_raw_ns();
457+
active_duration = user->active_duration_ns - last_active_duration;
458+
/* emit the gpu work period event */
459+
trace_gpu_work_period(gpuid, uid, start_time, end_time, active_duration);
460+
user->last_timestamp_ns = end_time;
461+
}
462+
463+
/**
464+
* xe_user_alloc() - Allocate xe user
465+
* @void: No arg
466+
*
467+
* Allocate xe user struct to track activity on the gpu
468+
* by the application. Call this API whenever a new app
469+
* has opened xe device.
470+
*
471+
* Return: pointer to user struct or NULL if can't allocate
472+
*/
473+
struct xe_user *xe_user_alloc(const unsigned int uid)
474+
{
475+
struct xe_user *user;
476+
477+
user = kzalloc(sizeof(*user), GFP_KERNEL);
478+
if (!user || !uid)
479+
return NULL;
480+
481+
user->uid = uid;
482+
kref_init(&user->kref);
483+
spin_lock_init(&user->filelist_lock);
484+
INIT_LIST_HEAD(&user->filelist);
485+
INIT_LIST_HEAD(&user->entry);
486+
INIT_WORK(&user->work, work_period_worker);
487+
return user;
488+
}
489+
490+
/**
491+
* __xe_user_free() - Free user struct
492+
* @kref: The reference
493+
*
494+
* destroy this xe user when the last xe file associated with
495+
* this xe user is destroyed
496+
*
497+
* Return: void
498+
*/
499+
void __xe_user_free(struct kref *kref)
500+
{
501+
struct xe_user *user =
502+
container_of(kref, typeof(*user), kref);
503+
504+
list_del(&user->entry);
505+
kfree(user);
506+
}

drivers/gpu/drm/xe/xe_drm_client.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/rcupdate.h>
1313
#include <linux/sched.h>
1414
#include <linux/spinlock.h>
15+
#include <linux/workqueue.h>
1516

1617
struct drm_file;
1718
struct drm_printer;
@@ -34,6 +35,50 @@ struct xe_drm_client {
3435
#endif
3536
};
3637

38+
/**
39+
* This is a per process/user id structure for a xe device
40+
* client. It is allocated when a new process/app opens the
41+
* xe device and destroyed when the last xe file for this
42+
* process is destroyed
43+
*/
44+
struct xe_user {
45+
struct kref kref;
46+
struct xe_device *xe;
47+
/**
48+
* @filelist_lock: lock protecting the filelist list
49+
*/
50+
spinlock_t filelist_lock;
51+
/**
52+
* @filelist: list of xe files belonging to this process
53+
*/
54+
struct list_head filelist;
55+
/**
56+
* @entry: entry into the xe.work_period.user_list list
57+
*/
58+
struct list_head entry;
59+
/**
60+
* @work: work to emit the gpu work period event for this xe user
61+
*/
62+
struct work_struct work;
63+
/**
64+
* @uid: user id for this process/app
65+
*
66+
* In android each app has its own user id. So we use uid to identify
67+
* an app that is using the gpu
68+
*/
69+
u32 uid;
70+
/**
71+
* @active_duration_ns: sum total of xe_file.active_duration_ns for all
72+
* xe files belonging to this xe user
73+
*/
74+
u64 active_duration_ns;
75+
/**
76+
* @last_timestamp_ns: timestamp in ns when we last emitted event for
77+
* this xe user
78+
*/
79+
u64 last_timestamp_ns;
80+
};
81+
3782
static inline struct xe_drm_client *
3883
xe_drm_client_get(struct xe_drm_client *client)
3984
{
@@ -67,4 +112,20 @@ static inline void xe_drm_client_remove_bo(struct xe_bo *bo)
67112
{
68113
}
69114
#endif
115+
116+
struct xe_user *xe_user_alloc(const unsigned int uid);
117+
118+
static inline struct xe_user *
119+
xe_user_get(struct xe_user *user)
120+
{
121+
kref_get(&user->kref);
122+
return user;
123+
}
124+
125+
void __xe_user_free(struct kref *kref);
126+
127+
static inline void xe_user_put(struct xe_user *user)
128+
{
129+
kref_put(&user->kref, __xe_user_free);
130+
}
70131
#endif

0 commit comments

Comments
 (0)