1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun *
3*4882a593Smuzhiyun * (C) COPYRIGHT 2011-2017 ARM Limited. All rights reserved.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * This program is free software and is provided to you under the terms of the
6*4882a593Smuzhiyun * GNU General Public License version 2 as published by the Free Software
7*4882a593Smuzhiyun * Foundation, and any use by you of this program is subject to the terms
8*4882a593Smuzhiyun * of such GNU licence.
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * A copy of the licence is included with the program, and can also be obtained
11*4882a593Smuzhiyun * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
12*4882a593Smuzhiyun * Boston, MA 02110-1301, USA.
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun */
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #include <linux/anon_inodes.h>
19*4882a593Smuzhiyun #include <linux/atomic.h>
20*4882a593Smuzhiyun #include <linux/hrtimer.h>
21*4882a593Smuzhiyun #include <linux/jiffies.h>
22*4882a593Smuzhiyun #include <linux/kthread.h>
23*4882a593Smuzhiyun #include <linux/list.h>
24*4882a593Smuzhiyun #include <linux/mm.h>
25*4882a593Smuzhiyun #include <linux/poll.h>
26*4882a593Smuzhiyun #include <linux/preempt.h>
27*4882a593Smuzhiyun #include <linux/slab.h>
28*4882a593Smuzhiyun #include <linux/wait.h>
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #include <mali_kbase.h>
31*4882a593Smuzhiyun #include <mali_kbase_hwaccess_instr.h>
32*4882a593Smuzhiyun #include <mali_kbase_hwaccess_jm.h>
33*4882a593Smuzhiyun #include <mali_kbase_hwcnt_reader.h>
34*4882a593Smuzhiyun #include <mali_kbase_mem_linux.h>
35*4882a593Smuzhiyun #include <mali_kbase_tlstream.h>
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun /*****************************************************************************/
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun /* Hwcnt reader API version */
40*4882a593Smuzhiyun #define HWCNT_READER_API 1
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun /* The number of nanoseconds in a second. */
43*4882a593Smuzhiyun #define NSECS_IN_SEC 1000000000ull /* ns */
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /* The time resolution of dumping service. */
46*4882a593Smuzhiyun #define DUMPING_RESOLUTION 500000ull /* ns */
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun /* The maximal supported number of dumping buffers. */
49*4882a593Smuzhiyun #define MAX_BUFFER_COUNT 32
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun /* Size and number of hw counters blocks. */
52*4882a593Smuzhiyun #define NR_CNT_BLOCKS_PER_GROUP 8
53*4882a593Smuzhiyun #define NR_CNT_PER_BLOCK 64
54*4882a593Smuzhiyun #define NR_BYTES_PER_CNT 4
55*4882a593Smuzhiyun #define NR_BYTES_PER_HDR 16
56*4882a593Smuzhiyun #define PRFCNT_EN_MASK_OFFSET 0x8
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun /*****************************************************************************/
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun enum {
61*4882a593Smuzhiyun SHADER_HWCNT_BM,
62*4882a593Smuzhiyun TILER_HWCNT_BM,
63*4882a593Smuzhiyun MMU_L2_HWCNT_BM,
64*4882a593Smuzhiyun JM_HWCNT_BM
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun enum vinstr_state {
68*4882a593Smuzhiyun VINSTR_IDLE,
69*4882a593Smuzhiyun VINSTR_DUMPING,
70*4882a593Smuzhiyun VINSTR_SUSPENDING,
71*4882a593Smuzhiyun VINSTR_SUSPENDED,
72*4882a593Smuzhiyun VINSTR_RESUMING
73*4882a593Smuzhiyun };
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun /**
76*4882a593Smuzhiyun * struct kbase_vinstr_context - vinstr context per device
77*4882a593Smuzhiyun * @lock: protects the entire vinstr context
78*4882a593Smuzhiyun * @kbdev: pointer to kbase device
79*4882a593Smuzhiyun * @kctx: pointer to kbase context
80*4882a593Smuzhiyun * @vmap: vinstr vmap for mapping hwcnt dump buffer
81*4882a593Smuzhiyun * @gpu_va: GPU hwcnt dump buffer address
82*4882a593Smuzhiyun * @cpu_va: the CPU side mapping of the hwcnt dump buffer
83*4882a593Smuzhiyun * @dump_size: size of the dump buffer in bytes
84*4882a593Smuzhiyun * @bitmap: current set of counters monitored, not always in sync
85*4882a593Smuzhiyun * with hardware
86*4882a593Smuzhiyun * @reprogram: when true, reprogram hwcnt block with the new set of
87*4882a593Smuzhiyun * counters
88*4882a593Smuzhiyun * @state: vinstr state
89*4882a593Smuzhiyun * @state_lock: protects information about vinstr state
90*4882a593Smuzhiyun * @suspend_waitq: notification queue to trigger state re-validation
91*4882a593Smuzhiyun * @suspend_cnt: reference counter of vinstr's suspend state
92*4882a593Smuzhiyun * @suspend_work: worker to execute on entering suspended state
93*4882a593Smuzhiyun * @resume_work: worker to execute on leaving suspended state
94*4882a593Smuzhiyun * @nclients: number of attached clients, pending or otherwise
95*4882a593Smuzhiyun * @waiting_clients: head of list of clients being periodically sampled
96*4882a593Smuzhiyun * @idle_clients: head of list of clients being idle
97*4882a593Smuzhiyun * @suspended_clients: head of list of clients being suspended
98*4882a593Smuzhiyun * @thread: periodic sampling thread
99*4882a593Smuzhiyun * @waitq: notification queue of sampling thread
100*4882a593Smuzhiyun * @request_pending: request for action for sampling thread
101*4882a593Smuzhiyun */
102*4882a593Smuzhiyun struct kbase_vinstr_context {
103*4882a593Smuzhiyun struct mutex lock;
104*4882a593Smuzhiyun struct kbase_device *kbdev;
105*4882a593Smuzhiyun struct kbase_context *kctx;
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun struct kbase_vmap_struct vmap;
108*4882a593Smuzhiyun u64 gpu_va;
109*4882a593Smuzhiyun void *cpu_va;
110*4882a593Smuzhiyun size_t dump_size;
111*4882a593Smuzhiyun u32 bitmap[4];
112*4882a593Smuzhiyun bool reprogram;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun enum vinstr_state state;
115*4882a593Smuzhiyun struct spinlock state_lock;
116*4882a593Smuzhiyun wait_queue_head_t suspend_waitq;
117*4882a593Smuzhiyun unsigned int suspend_cnt;
118*4882a593Smuzhiyun struct work_struct suspend_work;
119*4882a593Smuzhiyun struct work_struct resume_work;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun u32 nclients;
122*4882a593Smuzhiyun struct list_head waiting_clients;
123*4882a593Smuzhiyun struct list_head idle_clients;
124*4882a593Smuzhiyun struct list_head suspended_clients;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun struct task_struct *thread;
127*4882a593Smuzhiyun wait_queue_head_t waitq;
128*4882a593Smuzhiyun atomic_t request_pending;
129*4882a593Smuzhiyun };
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun /**
132*4882a593Smuzhiyun * struct kbase_vinstr_client - a vinstr client attached to a vinstr context
133*4882a593Smuzhiyun * @vinstr_ctx: vinstr context client is attached to
134*4882a593Smuzhiyun * @list: node used to attach this client to list in vinstr context
135*4882a593Smuzhiyun * @buffer_count: number of buffers this client is using
136*4882a593Smuzhiyun * @event_mask: events this client reacts to
137*4882a593Smuzhiyun * @dump_size: size of one dump buffer in bytes
138*4882a593Smuzhiyun * @bitmap: bitmap request for JM, TILER, SHADER and MMU counters
139*4882a593Smuzhiyun * @legacy_buffer: userspace hwcnt dump buffer (legacy interface)
140*4882a593Smuzhiyun * @kernel_buffer: kernel hwcnt dump buffer (kernel client interface)
141*4882a593Smuzhiyun * @accum_buffer: temporary accumulation buffer for preserving counters
142*4882a593Smuzhiyun * @dump_time: next time this clients shall request hwcnt dump
143*4882a593Smuzhiyun * @dump_interval: interval between periodic hwcnt dumps
144*4882a593Smuzhiyun * @dump_buffers: kernel hwcnt dump buffers allocated by this client
145*4882a593Smuzhiyun * @dump_buffers_meta: metadata of dump buffers
146*4882a593Smuzhiyun * @meta_idx: index of metadata being accessed by userspace
147*4882a593Smuzhiyun * @read_idx: index of buffer read by userspace
148*4882a593Smuzhiyun * @write_idx: index of buffer being written by dumping service
149*4882a593Smuzhiyun * @waitq: client's notification queue
150*4882a593Smuzhiyun * @pending: when true, client has attached but hwcnt not yet updated
151*4882a593Smuzhiyun */
152*4882a593Smuzhiyun struct kbase_vinstr_client {
153*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx;
154*4882a593Smuzhiyun struct list_head list;
155*4882a593Smuzhiyun unsigned int buffer_count;
156*4882a593Smuzhiyun u32 event_mask;
157*4882a593Smuzhiyun size_t dump_size;
158*4882a593Smuzhiyun u32 bitmap[4];
159*4882a593Smuzhiyun void __user *legacy_buffer;
160*4882a593Smuzhiyun void *kernel_buffer;
161*4882a593Smuzhiyun void *accum_buffer;
162*4882a593Smuzhiyun u64 dump_time;
163*4882a593Smuzhiyun u32 dump_interval;
164*4882a593Smuzhiyun char *dump_buffers;
165*4882a593Smuzhiyun struct kbase_hwcnt_reader_metadata *dump_buffers_meta;
166*4882a593Smuzhiyun atomic_t meta_idx;
167*4882a593Smuzhiyun atomic_t read_idx;
168*4882a593Smuzhiyun atomic_t write_idx;
169*4882a593Smuzhiyun wait_queue_head_t waitq;
170*4882a593Smuzhiyun bool pending;
171*4882a593Smuzhiyun };
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun /**
174*4882a593Smuzhiyun * struct kbasep_vinstr_wake_up_timer - vinstr service thread wake up timer
175*4882a593Smuzhiyun * @hrtimer: high resolution timer
176*4882a593Smuzhiyun * @vinstr_ctx: vinstr context
177*4882a593Smuzhiyun */
178*4882a593Smuzhiyun struct kbasep_vinstr_wake_up_timer {
179*4882a593Smuzhiyun struct hrtimer hrtimer;
180*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx;
181*4882a593Smuzhiyun };
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun /*****************************************************************************/
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun static int kbasep_vinstr_service_task(void *data);
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun static unsigned int kbasep_vinstr_hwcnt_reader_poll(
188*4882a593Smuzhiyun struct file *filp,
189*4882a593Smuzhiyun poll_table *wait);
190*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl(
191*4882a593Smuzhiyun struct file *filp,
192*4882a593Smuzhiyun unsigned int cmd,
193*4882a593Smuzhiyun unsigned long arg);
194*4882a593Smuzhiyun static int kbasep_vinstr_hwcnt_reader_mmap(
195*4882a593Smuzhiyun struct file *filp,
196*4882a593Smuzhiyun struct vm_area_struct *vma);
197*4882a593Smuzhiyun static int kbasep_vinstr_hwcnt_reader_release(
198*4882a593Smuzhiyun struct inode *inode,
199*4882a593Smuzhiyun struct file *filp);
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun /* The timeline stream file operations structure. */
202*4882a593Smuzhiyun static const struct file_operations vinstr_client_fops = {
203*4882a593Smuzhiyun .poll = kbasep_vinstr_hwcnt_reader_poll,
204*4882a593Smuzhiyun .unlocked_ioctl = kbasep_vinstr_hwcnt_reader_ioctl,
205*4882a593Smuzhiyun .compat_ioctl = kbasep_vinstr_hwcnt_reader_ioctl,
206*4882a593Smuzhiyun .mmap = kbasep_vinstr_hwcnt_reader_mmap,
207*4882a593Smuzhiyun .release = kbasep_vinstr_hwcnt_reader_release,
208*4882a593Smuzhiyun };
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun /*****************************************************************************/
211*4882a593Smuzhiyun
enable_hwcnt(struct kbase_vinstr_context * vinstr_ctx)212*4882a593Smuzhiyun static int enable_hwcnt(struct kbase_vinstr_context *vinstr_ctx)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun struct kbase_context *kctx = vinstr_ctx->kctx;
215*4882a593Smuzhiyun struct kbase_device *kbdev = kctx->kbdev;
216*4882a593Smuzhiyun struct kbase_uk_hwcnt_setup setup;
217*4882a593Smuzhiyun int err;
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun setup.dump_buffer = vinstr_ctx->gpu_va;
220*4882a593Smuzhiyun setup.jm_bm = vinstr_ctx->bitmap[JM_HWCNT_BM];
221*4882a593Smuzhiyun setup.tiler_bm = vinstr_ctx->bitmap[TILER_HWCNT_BM];
222*4882a593Smuzhiyun setup.shader_bm = vinstr_ctx->bitmap[SHADER_HWCNT_BM];
223*4882a593Smuzhiyun setup.mmu_l2_bm = vinstr_ctx->bitmap[MMU_L2_HWCNT_BM];
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun /* Mark the context as active so the GPU is kept turned on */
226*4882a593Smuzhiyun /* A suspend won't happen here, because we're in a syscall from a
227*4882a593Smuzhiyun * userspace thread. */
228*4882a593Smuzhiyun kbase_pm_context_active(kbdev);
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun /* Schedule the context in */
231*4882a593Smuzhiyun kbasep_js_schedule_privileged_ctx(kbdev, kctx);
232*4882a593Smuzhiyun err = kbase_instr_hwcnt_enable_internal(kbdev, kctx, &setup);
233*4882a593Smuzhiyun if (err) {
234*4882a593Smuzhiyun /* Release the context. This had its own Power Manager Active
235*4882a593Smuzhiyun * reference */
236*4882a593Smuzhiyun kbasep_js_release_privileged_ctx(kbdev, kctx);
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun /* Also release our Power Manager Active reference */
239*4882a593Smuzhiyun kbase_pm_context_idle(kbdev);
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun return err;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun
disable_hwcnt(struct kbase_vinstr_context * vinstr_ctx)245*4882a593Smuzhiyun static void disable_hwcnt(struct kbase_vinstr_context *vinstr_ctx)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun struct kbase_context *kctx = vinstr_ctx->kctx;
248*4882a593Smuzhiyun struct kbase_device *kbdev = kctx->kbdev;
249*4882a593Smuzhiyun int err;
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun err = kbase_instr_hwcnt_disable_internal(kctx);
252*4882a593Smuzhiyun if (err) {
253*4882a593Smuzhiyun dev_warn(kbdev->dev, "Failed to disable HW counters (ctx:%p)",
254*4882a593Smuzhiyun kctx);
255*4882a593Smuzhiyun return;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun /* Release the context. This had its own Power Manager Active reference. */
259*4882a593Smuzhiyun kbasep_js_release_privileged_ctx(kbdev, kctx);
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun /* Also release our Power Manager Active reference. */
262*4882a593Smuzhiyun kbase_pm_context_idle(kbdev);
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun dev_dbg(kbdev->dev, "HW counters dumping disabled for context %p", kctx);
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun
reprogram_hwcnt(struct kbase_vinstr_context * vinstr_ctx)267*4882a593Smuzhiyun static int reprogram_hwcnt(struct kbase_vinstr_context *vinstr_ctx)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun disable_hwcnt(vinstr_ctx);
270*4882a593Smuzhiyun return enable_hwcnt(vinstr_ctx);
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun
hwcnt_bitmap_set(u32 dst[4],u32 src[4])273*4882a593Smuzhiyun static void hwcnt_bitmap_set(u32 dst[4], u32 src[4])
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun dst[JM_HWCNT_BM] = src[JM_HWCNT_BM];
276*4882a593Smuzhiyun dst[TILER_HWCNT_BM] = src[TILER_HWCNT_BM];
277*4882a593Smuzhiyun dst[SHADER_HWCNT_BM] = src[SHADER_HWCNT_BM];
278*4882a593Smuzhiyun dst[MMU_L2_HWCNT_BM] = src[MMU_L2_HWCNT_BM];
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
hwcnt_bitmap_union(u32 dst[4],u32 src[4])281*4882a593Smuzhiyun static void hwcnt_bitmap_union(u32 dst[4], u32 src[4])
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun dst[JM_HWCNT_BM] |= src[JM_HWCNT_BM];
284*4882a593Smuzhiyun dst[TILER_HWCNT_BM] |= src[TILER_HWCNT_BM];
285*4882a593Smuzhiyun dst[SHADER_HWCNT_BM] |= src[SHADER_HWCNT_BM];
286*4882a593Smuzhiyun dst[MMU_L2_HWCNT_BM] |= src[MMU_L2_HWCNT_BM];
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun
kbase_vinstr_dump_size(struct kbase_device * kbdev)289*4882a593Smuzhiyun size_t kbase_vinstr_dump_size(struct kbase_device *kbdev)
290*4882a593Smuzhiyun {
291*4882a593Smuzhiyun size_t dump_size;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun #ifndef CONFIG_MALI_NO_MALI
294*4882a593Smuzhiyun if (kbase_hw_has_feature(kbdev, BASE_HW_FEATURE_V4)) {
295*4882a593Smuzhiyun u32 nr_cg;
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun nr_cg = kbdev->gpu_props.num_core_groups;
298*4882a593Smuzhiyun dump_size = nr_cg * NR_CNT_BLOCKS_PER_GROUP *
299*4882a593Smuzhiyun NR_CNT_PER_BLOCK *
300*4882a593Smuzhiyun NR_BYTES_PER_CNT;
301*4882a593Smuzhiyun } else
302*4882a593Smuzhiyun #endif /* CONFIG_MALI_NO_MALI */
303*4882a593Smuzhiyun {
304*4882a593Smuzhiyun /* assume v5 for now */
305*4882a593Smuzhiyun base_gpu_props *props = &kbdev->gpu_props.props;
306*4882a593Smuzhiyun u32 nr_l2 = props->l2_props.num_l2_slices;
307*4882a593Smuzhiyun u64 core_mask = props->coherency_info.group[0].core_mask;
308*4882a593Smuzhiyun u32 nr_blocks = fls64(core_mask);
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun /* JM and tiler counter blocks are always present */
311*4882a593Smuzhiyun dump_size = (2 + nr_l2 + nr_blocks) *
312*4882a593Smuzhiyun NR_CNT_PER_BLOCK *
313*4882a593Smuzhiyun NR_BYTES_PER_CNT;
314*4882a593Smuzhiyun }
315*4882a593Smuzhiyun return dump_size;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun KBASE_EXPORT_TEST_API(kbase_vinstr_dump_size);
318*4882a593Smuzhiyun
kbasep_vinstr_dump_size_ctx(struct kbase_vinstr_context * vinstr_ctx)319*4882a593Smuzhiyun static size_t kbasep_vinstr_dump_size_ctx(
320*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun return kbase_vinstr_dump_size(vinstr_ctx->kctx->kbdev);
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
kbasep_vinstr_map_kernel_dump_buffer(struct kbase_vinstr_context * vinstr_ctx)325*4882a593Smuzhiyun static int kbasep_vinstr_map_kernel_dump_buffer(
326*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx)
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun struct kbase_va_region *reg;
329*4882a593Smuzhiyun struct kbase_context *kctx = vinstr_ctx->kctx;
330*4882a593Smuzhiyun u64 flags, nr_pages;
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_WR;
333*4882a593Smuzhiyun vinstr_ctx->dump_size = kbasep_vinstr_dump_size_ctx(vinstr_ctx);
334*4882a593Smuzhiyun nr_pages = PFN_UP(vinstr_ctx->dump_size);
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun reg = kbase_mem_alloc(kctx, nr_pages, nr_pages, 0, &flags,
337*4882a593Smuzhiyun &vinstr_ctx->gpu_va);
338*4882a593Smuzhiyun if (!reg)
339*4882a593Smuzhiyun return -ENOMEM;
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun vinstr_ctx->cpu_va = kbase_vmap(
342*4882a593Smuzhiyun kctx,
343*4882a593Smuzhiyun vinstr_ctx->gpu_va,
344*4882a593Smuzhiyun vinstr_ctx->dump_size,
345*4882a593Smuzhiyun &vinstr_ctx->vmap);
346*4882a593Smuzhiyun if (!vinstr_ctx->cpu_va) {
347*4882a593Smuzhiyun kbase_mem_free(kctx, vinstr_ctx->gpu_va);
348*4882a593Smuzhiyun return -ENOMEM;
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun return 0;
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun
kbasep_vinstr_unmap_kernel_dump_buffer(struct kbase_vinstr_context * vinstr_ctx)354*4882a593Smuzhiyun static void kbasep_vinstr_unmap_kernel_dump_buffer(
355*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun struct kbase_context *kctx = vinstr_ctx->kctx;
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun kbase_vunmap(kctx, &vinstr_ctx->vmap);
360*4882a593Smuzhiyun kbase_mem_free(kctx, vinstr_ctx->gpu_va);
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun /**
364*4882a593Smuzhiyun * kbasep_vinstr_create_kctx - create kernel context for vinstr
365*4882a593Smuzhiyun * @vinstr_ctx: vinstr context
366*4882a593Smuzhiyun * Return: zero on success
367*4882a593Smuzhiyun */
kbasep_vinstr_create_kctx(struct kbase_vinstr_context * vinstr_ctx)368*4882a593Smuzhiyun static int kbasep_vinstr_create_kctx(struct kbase_vinstr_context *vinstr_ctx)
369*4882a593Smuzhiyun {
370*4882a593Smuzhiyun struct kbase_device *kbdev = vinstr_ctx->kbdev;
371*4882a593Smuzhiyun struct kbasep_kctx_list_element *element;
372*4882a593Smuzhiyun unsigned long flags;
373*4882a593Smuzhiyun bool enable_backend = false;
374*4882a593Smuzhiyun int err;
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun vinstr_ctx->kctx = kbase_create_context(vinstr_ctx->kbdev, true);
377*4882a593Smuzhiyun if (!vinstr_ctx->kctx)
378*4882a593Smuzhiyun return -ENOMEM;
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun /* Map the master kernel dump buffer. The HW dumps the counters
381*4882a593Smuzhiyun * into this memory region. */
382*4882a593Smuzhiyun err = kbasep_vinstr_map_kernel_dump_buffer(vinstr_ctx);
383*4882a593Smuzhiyun if (err) {
384*4882a593Smuzhiyun kbase_destroy_context(vinstr_ctx->kctx);
385*4882a593Smuzhiyun vinstr_ctx->kctx = NULL;
386*4882a593Smuzhiyun return err;
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun /* Add kernel context to list of contexts associated with device. */
390*4882a593Smuzhiyun element = kzalloc(sizeof(*element), GFP_KERNEL);
391*4882a593Smuzhiyun if (element) {
392*4882a593Smuzhiyun element->kctx = vinstr_ctx->kctx;
393*4882a593Smuzhiyun mutex_lock(&kbdev->kctx_list_lock);
394*4882a593Smuzhiyun list_add(&element->link, &kbdev->kctx_list);
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun /* Inform timeline client about new context.
397*4882a593Smuzhiyun * Do this while holding the lock to avoid tracepoint
398*4882a593Smuzhiyun * being created in both body and summary stream. */
399*4882a593Smuzhiyun KBASE_TLSTREAM_TL_NEW_CTX(
400*4882a593Smuzhiyun vinstr_ctx->kctx,
401*4882a593Smuzhiyun (u32)(vinstr_ctx->kctx->id),
402*4882a593Smuzhiyun (u32)(vinstr_ctx->kctx->tgid));
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun mutex_unlock(&kbdev->kctx_list_lock);
405*4882a593Smuzhiyun } else {
406*4882a593Smuzhiyun /* Don't treat this as a fail - just warn about it. */
407*4882a593Smuzhiyun dev_warn(kbdev->dev,
408*4882a593Smuzhiyun "couldn't add kctx to kctx_list\n");
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun /* Don't enable hardware counters if vinstr is suspended.
412*4882a593Smuzhiyun * Note that vinstr resume code is run under vinstr context lock,
413*4882a593Smuzhiyun * lower layer will be enabled as needed on resume. */
414*4882a593Smuzhiyun spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
415*4882a593Smuzhiyun if (VINSTR_IDLE == vinstr_ctx->state)
416*4882a593Smuzhiyun enable_backend = true;
417*4882a593Smuzhiyun spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
418*4882a593Smuzhiyun if (enable_backend)
419*4882a593Smuzhiyun err = enable_hwcnt(vinstr_ctx);
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun if (err) {
422*4882a593Smuzhiyun kbasep_vinstr_unmap_kernel_dump_buffer(vinstr_ctx);
423*4882a593Smuzhiyun kbase_destroy_context(vinstr_ctx->kctx);
424*4882a593Smuzhiyun if (element) {
425*4882a593Smuzhiyun mutex_lock(&kbdev->kctx_list_lock);
426*4882a593Smuzhiyun list_del(&element->link);
427*4882a593Smuzhiyun kfree(element);
428*4882a593Smuzhiyun mutex_unlock(&kbdev->kctx_list_lock);
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun KBASE_TLSTREAM_TL_DEL_CTX(vinstr_ctx->kctx);
431*4882a593Smuzhiyun vinstr_ctx->kctx = NULL;
432*4882a593Smuzhiyun return err;
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun vinstr_ctx->thread = kthread_run(
436*4882a593Smuzhiyun kbasep_vinstr_service_task,
437*4882a593Smuzhiyun vinstr_ctx,
438*4882a593Smuzhiyun "mali_vinstr_service");
439*4882a593Smuzhiyun if (!vinstr_ctx->thread) {
440*4882a593Smuzhiyun disable_hwcnt(vinstr_ctx);
441*4882a593Smuzhiyun kbasep_vinstr_unmap_kernel_dump_buffer(vinstr_ctx);
442*4882a593Smuzhiyun kbase_destroy_context(vinstr_ctx->kctx);
443*4882a593Smuzhiyun if (element) {
444*4882a593Smuzhiyun mutex_lock(&kbdev->kctx_list_lock);
445*4882a593Smuzhiyun list_del(&element->link);
446*4882a593Smuzhiyun kfree(element);
447*4882a593Smuzhiyun mutex_unlock(&kbdev->kctx_list_lock);
448*4882a593Smuzhiyun }
449*4882a593Smuzhiyun KBASE_TLSTREAM_TL_DEL_CTX(vinstr_ctx->kctx);
450*4882a593Smuzhiyun vinstr_ctx->kctx = NULL;
451*4882a593Smuzhiyun return -EFAULT;
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun return 0;
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun /**
458*4882a593Smuzhiyun * kbasep_vinstr_destroy_kctx - destroy vinstr's kernel context
459*4882a593Smuzhiyun * @vinstr_ctx: vinstr context
460*4882a593Smuzhiyun */
kbasep_vinstr_destroy_kctx(struct kbase_vinstr_context * vinstr_ctx)461*4882a593Smuzhiyun static void kbasep_vinstr_destroy_kctx(struct kbase_vinstr_context *vinstr_ctx)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun struct kbase_device *kbdev = vinstr_ctx->kbdev;
464*4882a593Smuzhiyun struct kbasep_kctx_list_element *element;
465*4882a593Smuzhiyun struct kbasep_kctx_list_element *tmp;
466*4882a593Smuzhiyun bool found = false;
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun /* Release hw counters dumping resources. */
469*4882a593Smuzhiyun vinstr_ctx->thread = NULL;
470*4882a593Smuzhiyun disable_hwcnt(vinstr_ctx);
471*4882a593Smuzhiyun kbasep_vinstr_unmap_kernel_dump_buffer(vinstr_ctx);
472*4882a593Smuzhiyun kbase_destroy_context(vinstr_ctx->kctx);
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun /* Remove kernel context from the device's contexts list. */
475*4882a593Smuzhiyun mutex_lock(&kbdev->kctx_list_lock);
476*4882a593Smuzhiyun list_for_each_entry_safe(element, tmp, &kbdev->kctx_list, link) {
477*4882a593Smuzhiyun if (element->kctx == vinstr_ctx->kctx) {
478*4882a593Smuzhiyun list_del(&element->link);
479*4882a593Smuzhiyun kfree(element);
480*4882a593Smuzhiyun found = true;
481*4882a593Smuzhiyun }
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun mutex_unlock(&kbdev->kctx_list_lock);
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun if (!found)
486*4882a593Smuzhiyun dev_warn(kbdev->dev, "kctx not in kctx_list\n");
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun /* Inform timeline client about context destruction. */
489*4882a593Smuzhiyun KBASE_TLSTREAM_TL_DEL_CTX(vinstr_ctx->kctx);
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun vinstr_ctx->kctx = NULL;
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun /**
495*4882a593Smuzhiyun * kbasep_vinstr_attach_client - Attach a client to the vinstr core
496*4882a593Smuzhiyun * @vinstr_ctx: vinstr context
497*4882a593Smuzhiyun * @buffer_count: requested number of dump buffers
498*4882a593Smuzhiyun * @bitmap: bitmaps describing which counters should be enabled
499*4882a593Smuzhiyun * @argp: pointer where notification descriptor shall be stored
500*4882a593Smuzhiyun * @kernel_buffer: pointer to kernel side buffer
501*4882a593Smuzhiyun *
502*4882a593Smuzhiyun * Return: vinstr opaque client handle or NULL on failure
503*4882a593Smuzhiyun */
kbasep_vinstr_attach_client(struct kbase_vinstr_context * vinstr_ctx,u32 buffer_count,u32 bitmap[4],void * argp,void * kernel_buffer)504*4882a593Smuzhiyun static struct kbase_vinstr_client *kbasep_vinstr_attach_client(
505*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx, u32 buffer_count,
506*4882a593Smuzhiyun u32 bitmap[4], void *argp, void *kernel_buffer)
507*4882a593Smuzhiyun {
508*4882a593Smuzhiyun struct task_struct *thread = NULL;
509*4882a593Smuzhiyun struct kbase_vinstr_client *cli;
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun if (buffer_count > MAX_BUFFER_COUNT
514*4882a593Smuzhiyun || (buffer_count & (buffer_count - 1)))
515*4882a593Smuzhiyun return NULL;
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun cli = kzalloc(sizeof(*cli), GFP_KERNEL);
518*4882a593Smuzhiyun if (!cli)
519*4882a593Smuzhiyun return NULL;
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun cli->vinstr_ctx = vinstr_ctx;
522*4882a593Smuzhiyun cli->buffer_count = buffer_count;
523*4882a593Smuzhiyun cli->event_mask =
524*4882a593Smuzhiyun (1 << BASE_HWCNT_READER_EVENT_MANUAL) |
525*4882a593Smuzhiyun (1 << BASE_HWCNT_READER_EVENT_PERIODIC);
526*4882a593Smuzhiyun cli->pending = true;
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun hwcnt_bitmap_set(cli->bitmap, bitmap);
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun mutex_lock(&vinstr_ctx->lock);
531*4882a593Smuzhiyun
532*4882a593Smuzhiyun hwcnt_bitmap_union(vinstr_ctx->bitmap, cli->bitmap);
533*4882a593Smuzhiyun vinstr_ctx->reprogram = true;
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun /* If this is the first client, create the vinstr kbase
536*4882a593Smuzhiyun * context. This context is permanently resident until the
537*4882a593Smuzhiyun * last client exits. */
538*4882a593Smuzhiyun if (!vinstr_ctx->nclients) {
539*4882a593Smuzhiyun hwcnt_bitmap_set(vinstr_ctx->bitmap, cli->bitmap);
540*4882a593Smuzhiyun if (kbasep_vinstr_create_kctx(vinstr_ctx) < 0)
541*4882a593Smuzhiyun goto error;
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun vinstr_ctx->reprogram = false;
544*4882a593Smuzhiyun cli->pending = false;
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun /* The GPU resets the counter block every time there is a request
548*4882a593Smuzhiyun * to dump it. We need a per client kernel buffer for accumulating
549*4882a593Smuzhiyun * the counters. */
550*4882a593Smuzhiyun cli->dump_size = kbasep_vinstr_dump_size_ctx(vinstr_ctx);
551*4882a593Smuzhiyun cli->accum_buffer = kzalloc(cli->dump_size, GFP_KERNEL);
552*4882a593Smuzhiyun if (!cli->accum_buffer)
553*4882a593Smuzhiyun goto error;
554*4882a593Smuzhiyun
555*4882a593Smuzhiyun /* Prepare buffers. */
556*4882a593Smuzhiyun if (cli->buffer_count) {
557*4882a593Smuzhiyun int *fd = (int *)argp;
558*4882a593Smuzhiyun size_t tmp;
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun /* Allocate area for buffers metadata storage. */
561*4882a593Smuzhiyun tmp = sizeof(struct kbase_hwcnt_reader_metadata) *
562*4882a593Smuzhiyun cli->buffer_count;
563*4882a593Smuzhiyun cli->dump_buffers_meta = kmalloc(tmp, GFP_KERNEL);
564*4882a593Smuzhiyun if (!cli->dump_buffers_meta)
565*4882a593Smuzhiyun goto error;
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun /* Allocate required number of dumping buffers. */
568*4882a593Smuzhiyun cli->dump_buffers = (char *)__get_free_pages(
569*4882a593Smuzhiyun GFP_KERNEL | __GFP_ZERO,
570*4882a593Smuzhiyun get_order(cli->dump_size * cli->buffer_count));
571*4882a593Smuzhiyun if (!cli->dump_buffers)
572*4882a593Smuzhiyun goto error;
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun /* Create descriptor for user-kernel data exchange. */
575*4882a593Smuzhiyun *fd = anon_inode_getfd(
576*4882a593Smuzhiyun "[mali_vinstr_desc]",
577*4882a593Smuzhiyun &vinstr_client_fops,
578*4882a593Smuzhiyun cli,
579*4882a593Smuzhiyun O_RDONLY | O_CLOEXEC);
580*4882a593Smuzhiyun if (0 > *fd)
581*4882a593Smuzhiyun goto error;
582*4882a593Smuzhiyun } else if (kernel_buffer) {
583*4882a593Smuzhiyun cli->kernel_buffer = kernel_buffer;
584*4882a593Smuzhiyun } else {
585*4882a593Smuzhiyun cli->legacy_buffer = (void __user *)argp;
586*4882a593Smuzhiyun }
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun atomic_set(&cli->read_idx, 0);
589*4882a593Smuzhiyun atomic_set(&cli->meta_idx, 0);
590*4882a593Smuzhiyun atomic_set(&cli->write_idx, 0);
591*4882a593Smuzhiyun init_waitqueue_head(&cli->waitq);
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun vinstr_ctx->nclients++;
594*4882a593Smuzhiyun list_add(&cli->list, &vinstr_ctx->idle_clients);
595*4882a593Smuzhiyun
596*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
597*4882a593Smuzhiyun
598*4882a593Smuzhiyun return cli;
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun error:
601*4882a593Smuzhiyun kfree(cli->dump_buffers_meta);
602*4882a593Smuzhiyun if (cli->dump_buffers)
603*4882a593Smuzhiyun free_pages(
604*4882a593Smuzhiyun (unsigned long)cli->dump_buffers,
605*4882a593Smuzhiyun get_order(cli->dump_size * cli->buffer_count));
606*4882a593Smuzhiyun kfree(cli->accum_buffer);
607*4882a593Smuzhiyun if (!vinstr_ctx->nclients && vinstr_ctx->kctx) {
608*4882a593Smuzhiyun thread = vinstr_ctx->thread;
609*4882a593Smuzhiyun kbasep_vinstr_destroy_kctx(vinstr_ctx);
610*4882a593Smuzhiyun }
611*4882a593Smuzhiyun kfree(cli);
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun /* Thread must be stopped after lock is released. */
616*4882a593Smuzhiyun if (thread)
617*4882a593Smuzhiyun kthread_stop(thread);
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun return NULL;
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun
kbase_vinstr_detach_client(struct kbase_vinstr_client * cli)622*4882a593Smuzhiyun void kbase_vinstr_detach_client(struct kbase_vinstr_client *cli)
623*4882a593Smuzhiyun {
624*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx;
625*4882a593Smuzhiyun struct kbase_vinstr_client *iter, *tmp;
626*4882a593Smuzhiyun struct task_struct *thread = NULL;
627*4882a593Smuzhiyun u32 zerobitmap[4] = { 0 };
628*4882a593Smuzhiyun int cli_found = 0;
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(cli);
631*4882a593Smuzhiyun vinstr_ctx = cli->vinstr_ctx;
632*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun mutex_lock(&vinstr_ctx->lock);
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun list_for_each_entry_safe(iter, tmp, &vinstr_ctx->idle_clients, list) {
637*4882a593Smuzhiyun if (iter == cli) {
638*4882a593Smuzhiyun vinstr_ctx->reprogram = true;
639*4882a593Smuzhiyun cli_found = 1;
640*4882a593Smuzhiyun list_del(&iter->list);
641*4882a593Smuzhiyun break;
642*4882a593Smuzhiyun }
643*4882a593Smuzhiyun }
644*4882a593Smuzhiyun if (!cli_found) {
645*4882a593Smuzhiyun list_for_each_entry_safe(
646*4882a593Smuzhiyun iter, tmp, &vinstr_ctx->waiting_clients, list) {
647*4882a593Smuzhiyun if (iter == cli) {
648*4882a593Smuzhiyun vinstr_ctx->reprogram = true;
649*4882a593Smuzhiyun cli_found = 1;
650*4882a593Smuzhiyun list_del(&iter->list);
651*4882a593Smuzhiyun break;
652*4882a593Smuzhiyun }
653*4882a593Smuzhiyun }
654*4882a593Smuzhiyun }
655*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(cli_found);
656*4882a593Smuzhiyun
657*4882a593Smuzhiyun kfree(cli->dump_buffers_meta);
658*4882a593Smuzhiyun free_pages(
659*4882a593Smuzhiyun (unsigned long)cli->dump_buffers,
660*4882a593Smuzhiyun get_order(cli->dump_size * cli->buffer_count));
661*4882a593Smuzhiyun kfree(cli->accum_buffer);
662*4882a593Smuzhiyun kfree(cli);
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun vinstr_ctx->nclients--;
665*4882a593Smuzhiyun if (!vinstr_ctx->nclients) {
666*4882a593Smuzhiyun thread = vinstr_ctx->thread;
667*4882a593Smuzhiyun kbasep_vinstr_destroy_kctx(vinstr_ctx);
668*4882a593Smuzhiyun }
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun /* Rebuild context bitmap now that the client has detached */
671*4882a593Smuzhiyun hwcnt_bitmap_set(vinstr_ctx->bitmap, zerobitmap);
672*4882a593Smuzhiyun list_for_each_entry(iter, &vinstr_ctx->idle_clients, list)
673*4882a593Smuzhiyun hwcnt_bitmap_union(vinstr_ctx->bitmap, iter->bitmap);
674*4882a593Smuzhiyun list_for_each_entry(iter, &vinstr_ctx->waiting_clients, list)
675*4882a593Smuzhiyun hwcnt_bitmap_union(vinstr_ctx->bitmap, iter->bitmap);
676*4882a593Smuzhiyun
677*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
678*4882a593Smuzhiyun
679*4882a593Smuzhiyun /* Thread must be stopped after lock is released. */
680*4882a593Smuzhiyun if (thread)
681*4882a593Smuzhiyun kthread_stop(thread);
682*4882a593Smuzhiyun }
683*4882a593Smuzhiyun KBASE_EXPORT_TEST_API(kbase_vinstr_detach_client);
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun /* Accumulate counters in the dump buffer */
accum_dump_buffer(void * dst,void * src,size_t dump_size)686*4882a593Smuzhiyun static void accum_dump_buffer(void *dst, void *src, size_t dump_size)
687*4882a593Smuzhiyun {
688*4882a593Smuzhiyun size_t block_size = NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT;
689*4882a593Smuzhiyun u32 *d = dst;
690*4882a593Smuzhiyun u32 *s = src;
691*4882a593Smuzhiyun size_t i, j;
692*4882a593Smuzhiyun
693*4882a593Smuzhiyun for (i = 0; i < dump_size; i += block_size) {
694*4882a593Smuzhiyun /* skip over the header block */
695*4882a593Smuzhiyun d += NR_BYTES_PER_HDR / sizeof(u32);
696*4882a593Smuzhiyun s += NR_BYTES_PER_HDR / sizeof(u32);
697*4882a593Smuzhiyun for (j = 0; j < (block_size - NR_BYTES_PER_HDR) / sizeof(u32); j++) {
698*4882a593Smuzhiyun /* saturate result if addition would result in wraparound */
699*4882a593Smuzhiyun if (U32_MAX - *d < *s)
700*4882a593Smuzhiyun *d = U32_MAX;
701*4882a593Smuzhiyun else
702*4882a593Smuzhiyun *d += *s;
703*4882a593Smuzhiyun d++;
704*4882a593Smuzhiyun s++;
705*4882a593Smuzhiyun }
706*4882a593Smuzhiyun }
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun
709*4882a593Smuzhiyun /* This is the Midgard v4 patch function. It copies the headers for each
710*4882a593Smuzhiyun * of the defined blocks from the master kernel buffer and then patches up
711*4882a593Smuzhiyun * the performance counter enable mask for each of the blocks to exclude
712*4882a593Smuzhiyun * counters that were not requested by the client. */
patch_dump_buffer_hdr_v4(struct kbase_vinstr_context * vinstr_ctx,struct kbase_vinstr_client * cli)713*4882a593Smuzhiyun static void patch_dump_buffer_hdr_v4(
714*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx,
715*4882a593Smuzhiyun struct kbase_vinstr_client *cli)
716*4882a593Smuzhiyun {
717*4882a593Smuzhiyun u32 *mask;
718*4882a593Smuzhiyun u8 *dst = cli->accum_buffer;
719*4882a593Smuzhiyun u8 *src = vinstr_ctx->cpu_va;
720*4882a593Smuzhiyun u32 nr_cg = vinstr_ctx->kctx->kbdev->gpu_props.num_core_groups;
721*4882a593Smuzhiyun size_t i, group_size, group;
722*4882a593Smuzhiyun enum {
723*4882a593Smuzhiyun SC0_BASE = 0 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
724*4882a593Smuzhiyun SC1_BASE = 1 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
725*4882a593Smuzhiyun SC2_BASE = 2 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
726*4882a593Smuzhiyun SC3_BASE = 3 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
727*4882a593Smuzhiyun TILER_BASE = 4 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
728*4882a593Smuzhiyun MMU_L2_BASE = 5 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
729*4882a593Smuzhiyun JM_BASE = 7 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT
730*4882a593Smuzhiyun };
731*4882a593Smuzhiyun
732*4882a593Smuzhiyun group_size = NR_CNT_BLOCKS_PER_GROUP *
733*4882a593Smuzhiyun NR_CNT_PER_BLOCK *
734*4882a593Smuzhiyun NR_BYTES_PER_CNT;
735*4882a593Smuzhiyun for (i = 0; i < nr_cg; i++) {
736*4882a593Smuzhiyun group = i * group_size;
737*4882a593Smuzhiyun /* copy shader core headers */
738*4882a593Smuzhiyun memcpy(&dst[group + SC0_BASE], &src[group + SC0_BASE],
739*4882a593Smuzhiyun NR_BYTES_PER_HDR);
740*4882a593Smuzhiyun memcpy(&dst[group + SC1_BASE], &src[group + SC1_BASE],
741*4882a593Smuzhiyun NR_BYTES_PER_HDR);
742*4882a593Smuzhiyun memcpy(&dst[group + SC2_BASE], &src[group + SC2_BASE],
743*4882a593Smuzhiyun NR_BYTES_PER_HDR);
744*4882a593Smuzhiyun memcpy(&dst[group + SC3_BASE], &src[group + SC3_BASE],
745*4882a593Smuzhiyun NR_BYTES_PER_HDR);
746*4882a593Smuzhiyun
747*4882a593Smuzhiyun /* copy tiler header */
748*4882a593Smuzhiyun memcpy(&dst[group + TILER_BASE], &src[group + TILER_BASE],
749*4882a593Smuzhiyun NR_BYTES_PER_HDR);
750*4882a593Smuzhiyun
751*4882a593Smuzhiyun /* copy mmu header */
752*4882a593Smuzhiyun memcpy(&dst[group + MMU_L2_BASE], &src[group + MMU_L2_BASE],
753*4882a593Smuzhiyun NR_BYTES_PER_HDR);
754*4882a593Smuzhiyun
755*4882a593Smuzhiyun /* copy job manager header */
756*4882a593Smuzhiyun memcpy(&dst[group + JM_BASE], &src[group + JM_BASE],
757*4882a593Smuzhiyun NR_BYTES_PER_HDR);
758*4882a593Smuzhiyun
759*4882a593Smuzhiyun /* patch the shader core enable mask */
760*4882a593Smuzhiyun mask = (u32 *)&dst[group + SC0_BASE + PRFCNT_EN_MASK_OFFSET];
761*4882a593Smuzhiyun *mask &= cli->bitmap[SHADER_HWCNT_BM];
762*4882a593Smuzhiyun mask = (u32 *)&dst[group + SC1_BASE + PRFCNT_EN_MASK_OFFSET];
763*4882a593Smuzhiyun *mask &= cli->bitmap[SHADER_HWCNT_BM];
764*4882a593Smuzhiyun mask = (u32 *)&dst[group + SC2_BASE + PRFCNT_EN_MASK_OFFSET];
765*4882a593Smuzhiyun *mask &= cli->bitmap[SHADER_HWCNT_BM];
766*4882a593Smuzhiyun mask = (u32 *)&dst[group + SC3_BASE + PRFCNT_EN_MASK_OFFSET];
767*4882a593Smuzhiyun *mask &= cli->bitmap[SHADER_HWCNT_BM];
768*4882a593Smuzhiyun
769*4882a593Smuzhiyun /* patch the tiler core enable mask */
770*4882a593Smuzhiyun mask = (u32 *)&dst[group + TILER_BASE + PRFCNT_EN_MASK_OFFSET];
771*4882a593Smuzhiyun *mask &= cli->bitmap[TILER_HWCNT_BM];
772*4882a593Smuzhiyun
773*4882a593Smuzhiyun /* patch the mmu core enable mask */
774*4882a593Smuzhiyun mask = (u32 *)&dst[group + MMU_L2_BASE + PRFCNT_EN_MASK_OFFSET];
775*4882a593Smuzhiyun *mask &= cli->bitmap[MMU_L2_HWCNT_BM];
776*4882a593Smuzhiyun
777*4882a593Smuzhiyun /* patch the job manager enable mask */
778*4882a593Smuzhiyun mask = (u32 *)&dst[group + JM_BASE + PRFCNT_EN_MASK_OFFSET];
779*4882a593Smuzhiyun *mask &= cli->bitmap[JM_HWCNT_BM];
780*4882a593Smuzhiyun }
781*4882a593Smuzhiyun }
782*4882a593Smuzhiyun
783*4882a593Smuzhiyun /* This is the Midgard v5 patch function. It copies the headers for each
784*4882a593Smuzhiyun * of the defined blocks from the master kernel buffer and then patches up
785*4882a593Smuzhiyun * the performance counter enable mask for each of the blocks to exclude
786*4882a593Smuzhiyun * counters that were not requested by the client. */
patch_dump_buffer_hdr_v5(struct kbase_vinstr_context * vinstr_ctx,struct kbase_vinstr_client * cli)787*4882a593Smuzhiyun static void patch_dump_buffer_hdr_v5(
788*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx,
789*4882a593Smuzhiyun struct kbase_vinstr_client *cli)
790*4882a593Smuzhiyun {
791*4882a593Smuzhiyun struct kbase_device *kbdev = vinstr_ctx->kctx->kbdev;
792*4882a593Smuzhiyun u32 i, nr_l2;
793*4882a593Smuzhiyun u64 core_mask;
794*4882a593Smuzhiyun u32 *mask;
795*4882a593Smuzhiyun u8 *dst = cli->accum_buffer;
796*4882a593Smuzhiyun u8 *src = vinstr_ctx->cpu_va;
797*4882a593Smuzhiyun size_t block_size = NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT;
798*4882a593Smuzhiyun
799*4882a593Smuzhiyun /* copy and patch job manager header */
800*4882a593Smuzhiyun memcpy(dst, src, NR_BYTES_PER_HDR);
801*4882a593Smuzhiyun mask = (u32 *)&dst[PRFCNT_EN_MASK_OFFSET];
802*4882a593Smuzhiyun *mask &= cli->bitmap[JM_HWCNT_BM];
803*4882a593Smuzhiyun dst += block_size;
804*4882a593Smuzhiyun src += block_size;
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun /* copy and patch tiler header */
807*4882a593Smuzhiyun memcpy(dst, src, NR_BYTES_PER_HDR);
808*4882a593Smuzhiyun mask = (u32 *)&dst[PRFCNT_EN_MASK_OFFSET];
809*4882a593Smuzhiyun *mask &= cli->bitmap[TILER_HWCNT_BM];
810*4882a593Smuzhiyun dst += block_size;
811*4882a593Smuzhiyun src += block_size;
812*4882a593Smuzhiyun
813*4882a593Smuzhiyun /* copy and patch MMU/L2C headers */
814*4882a593Smuzhiyun nr_l2 = kbdev->gpu_props.props.l2_props.num_l2_slices;
815*4882a593Smuzhiyun for (i = 0; i < nr_l2; i++) {
816*4882a593Smuzhiyun memcpy(dst, src, NR_BYTES_PER_HDR);
817*4882a593Smuzhiyun mask = (u32 *)&dst[PRFCNT_EN_MASK_OFFSET];
818*4882a593Smuzhiyun *mask &= cli->bitmap[MMU_L2_HWCNT_BM];
819*4882a593Smuzhiyun dst += block_size;
820*4882a593Smuzhiyun src += block_size;
821*4882a593Smuzhiyun }
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun /* copy and patch shader core headers */
824*4882a593Smuzhiyun core_mask = kbdev->gpu_props.props.coherency_info.group[0].core_mask;
825*4882a593Smuzhiyun while (0ull != core_mask) {
826*4882a593Smuzhiyun memcpy(dst, src, NR_BYTES_PER_HDR);
827*4882a593Smuzhiyun if (0ull != (core_mask & 1ull)) {
828*4882a593Smuzhiyun /* if block is not reserved update header */
829*4882a593Smuzhiyun mask = (u32 *)&dst[PRFCNT_EN_MASK_OFFSET];
830*4882a593Smuzhiyun *mask &= cli->bitmap[SHADER_HWCNT_BM];
831*4882a593Smuzhiyun }
832*4882a593Smuzhiyun dst += block_size;
833*4882a593Smuzhiyun src += block_size;
834*4882a593Smuzhiyun
835*4882a593Smuzhiyun core_mask >>= 1;
836*4882a593Smuzhiyun }
837*4882a593Smuzhiyun }
838*4882a593Smuzhiyun
839*4882a593Smuzhiyun /**
840*4882a593Smuzhiyun * accum_clients - accumulate dumped hw counters for all known clients
841*4882a593Smuzhiyun * @vinstr_ctx: vinstr context
842*4882a593Smuzhiyun */
accum_clients(struct kbase_vinstr_context * vinstr_ctx)843*4882a593Smuzhiyun static void accum_clients(struct kbase_vinstr_context *vinstr_ctx)
844*4882a593Smuzhiyun {
845*4882a593Smuzhiyun struct kbase_vinstr_client *iter;
846*4882a593Smuzhiyun int v4 = 0;
847*4882a593Smuzhiyun
848*4882a593Smuzhiyun #ifndef CONFIG_MALI_NO_MALI
849*4882a593Smuzhiyun v4 = kbase_hw_has_feature(vinstr_ctx->kbdev, BASE_HW_FEATURE_V4);
850*4882a593Smuzhiyun #endif
851*4882a593Smuzhiyun
852*4882a593Smuzhiyun list_for_each_entry(iter, &vinstr_ctx->idle_clients, list) {
853*4882a593Smuzhiyun /* Don't bother accumulating clients whose hwcnt requests
854*4882a593Smuzhiyun * have not yet been honoured. */
855*4882a593Smuzhiyun if (iter->pending)
856*4882a593Smuzhiyun continue;
857*4882a593Smuzhiyun if (v4)
858*4882a593Smuzhiyun patch_dump_buffer_hdr_v4(vinstr_ctx, iter);
859*4882a593Smuzhiyun else
860*4882a593Smuzhiyun patch_dump_buffer_hdr_v5(vinstr_ctx, iter);
861*4882a593Smuzhiyun accum_dump_buffer(
862*4882a593Smuzhiyun iter->accum_buffer,
863*4882a593Smuzhiyun vinstr_ctx->cpu_va,
864*4882a593Smuzhiyun iter->dump_size);
865*4882a593Smuzhiyun }
866*4882a593Smuzhiyun list_for_each_entry(iter, &vinstr_ctx->waiting_clients, list) {
867*4882a593Smuzhiyun /* Don't bother accumulating clients whose hwcnt requests
868*4882a593Smuzhiyun * have not yet been honoured. */
869*4882a593Smuzhiyun if (iter->pending)
870*4882a593Smuzhiyun continue;
871*4882a593Smuzhiyun if (v4)
872*4882a593Smuzhiyun patch_dump_buffer_hdr_v4(vinstr_ctx, iter);
873*4882a593Smuzhiyun else
874*4882a593Smuzhiyun patch_dump_buffer_hdr_v5(vinstr_ctx, iter);
875*4882a593Smuzhiyun accum_dump_buffer(
876*4882a593Smuzhiyun iter->accum_buffer,
877*4882a593Smuzhiyun vinstr_ctx->cpu_va,
878*4882a593Smuzhiyun iter->dump_size);
879*4882a593Smuzhiyun }
880*4882a593Smuzhiyun }
881*4882a593Smuzhiyun
882*4882a593Smuzhiyun /*****************************************************************************/
883*4882a593Smuzhiyun
884*4882a593Smuzhiyun /**
885*4882a593Smuzhiyun * kbasep_vinstr_get_timestamp - return timestamp
886*4882a593Smuzhiyun *
887*4882a593Smuzhiyun * Function returns timestamp value based on raw monotonic timer. Value will
888*4882a593Smuzhiyun * wrap around zero in case of overflow.
889*4882a593Smuzhiyun *
890*4882a593Smuzhiyun * Return: timestamp value
891*4882a593Smuzhiyun */
kbasep_vinstr_get_timestamp(void)892*4882a593Smuzhiyun static u64 kbasep_vinstr_get_timestamp(void)
893*4882a593Smuzhiyun {
894*4882a593Smuzhiyun struct timespec64 ts;
895*4882a593Smuzhiyun
896*4882a593Smuzhiyun ktime_get_raw_ts64(&ts);
897*4882a593Smuzhiyun return (u64)ts.tv_sec * NSECS_IN_SEC + ts.tv_nsec;
898*4882a593Smuzhiyun }
899*4882a593Smuzhiyun
900*4882a593Smuzhiyun /**
901*4882a593Smuzhiyun * kbasep_vinstr_add_dump_request - register client's dumping request
902*4882a593Smuzhiyun * @cli: requesting client
903*4882a593Smuzhiyun * @waiting_clients: list of pending dumping requests
904*4882a593Smuzhiyun */
kbasep_vinstr_add_dump_request(struct kbase_vinstr_client * cli,struct list_head * waiting_clients)905*4882a593Smuzhiyun static void kbasep_vinstr_add_dump_request(
906*4882a593Smuzhiyun struct kbase_vinstr_client *cli,
907*4882a593Smuzhiyun struct list_head *waiting_clients)
908*4882a593Smuzhiyun {
909*4882a593Smuzhiyun struct kbase_vinstr_client *tmp;
910*4882a593Smuzhiyun
911*4882a593Smuzhiyun if (list_empty(waiting_clients)) {
912*4882a593Smuzhiyun list_add(&cli->list, waiting_clients);
913*4882a593Smuzhiyun return;
914*4882a593Smuzhiyun }
915*4882a593Smuzhiyun list_for_each_entry(tmp, waiting_clients, list) {
916*4882a593Smuzhiyun if (tmp->dump_time > cli->dump_time) {
917*4882a593Smuzhiyun list_add_tail(&cli->list, &tmp->list);
918*4882a593Smuzhiyun return;
919*4882a593Smuzhiyun }
920*4882a593Smuzhiyun }
921*4882a593Smuzhiyun list_add_tail(&cli->list, waiting_clients);
922*4882a593Smuzhiyun }
923*4882a593Smuzhiyun
924*4882a593Smuzhiyun /**
925*4882a593Smuzhiyun * kbasep_vinstr_collect_and_accumulate - collect hw counters via low level
926*4882a593Smuzhiyun * dump and accumulate them for known
927*4882a593Smuzhiyun * clients
928*4882a593Smuzhiyun * @vinstr_ctx: vinstr context
929*4882a593Smuzhiyun * @timestamp: pointer where collection timestamp will be recorded
930*4882a593Smuzhiyun *
931*4882a593Smuzhiyun * Return: zero on success
932*4882a593Smuzhiyun */
kbasep_vinstr_collect_and_accumulate(struct kbase_vinstr_context * vinstr_ctx,u64 * timestamp)933*4882a593Smuzhiyun static int kbasep_vinstr_collect_and_accumulate(
934*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx, u64 *timestamp)
935*4882a593Smuzhiyun {
936*4882a593Smuzhiyun unsigned long flags;
937*4882a593Smuzhiyun int rcode;
938*4882a593Smuzhiyun
939*4882a593Smuzhiyun #ifdef CONFIG_MALI_NO_MALI
940*4882a593Smuzhiyun /* The dummy model needs the CPU mapping. */
941*4882a593Smuzhiyun gpu_model_set_dummy_prfcnt_base_cpu(vinstr_ctx->cpu_va);
942*4882a593Smuzhiyun #endif
943*4882a593Smuzhiyun
944*4882a593Smuzhiyun spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
945*4882a593Smuzhiyun if (VINSTR_IDLE != vinstr_ctx->state) {
946*4882a593Smuzhiyun spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
947*4882a593Smuzhiyun return -EAGAIN;
948*4882a593Smuzhiyun } else {
949*4882a593Smuzhiyun vinstr_ctx->state = VINSTR_DUMPING;
950*4882a593Smuzhiyun }
951*4882a593Smuzhiyun spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun /* Request HW counters dump.
954*4882a593Smuzhiyun * Disable preemption to make dump timestamp more accurate. */
955*4882a593Smuzhiyun preempt_disable();
956*4882a593Smuzhiyun *timestamp = kbasep_vinstr_get_timestamp();
957*4882a593Smuzhiyun rcode = kbase_instr_hwcnt_request_dump(vinstr_ctx->kctx);
958*4882a593Smuzhiyun preempt_enable();
959*4882a593Smuzhiyun
960*4882a593Smuzhiyun if (!rcode)
961*4882a593Smuzhiyun rcode = kbase_instr_hwcnt_wait_for_dump(vinstr_ctx->kctx);
962*4882a593Smuzhiyun WARN_ON(rcode);
963*4882a593Smuzhiyun
964*4882a593Smuzhiyun spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
965*4882a593Smuzhiyun switch (vinstr_ctx->state)
966*4882a593Smuzhiyun {
967*4882a593Smuzhiyun case VINSTR_SUSPENDING:
968*4882a593Smuzhiyun schedule_work(&vinstr_ctx->suspend_work);
969*4882a593Smuzhiyun break;
970*4882a593Smuzhiyun case VINSTR_DUMPING:
971*4882a593Smuzhiyun vinstr_ctx->state = VINSTR_IDLE;
972*4882a593Smuzhiyun wake_up_all(&vinstr_ctx->suspend_waitq);
973*4882a593Smuzhiyun break;
974*4882a593Smuzhiyun default:
975*4882a593Smuzhiyun break;
976*4882a593Smuzhiyun }
977*4882a593Smuzhiyun spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
978*4882a593Smuzhiyun
979*4882a593Smuzhiyun /* Accumulate values of collected counters. */
980*4882a593Smuzhiyun if (!rcode)
981*4882a593Smuzhiyun accum_clients(vinstr_ctx);
982*4882a593Smuzhiyun
983*4882a593Smuzhiyun return rcode;
984*4882a593Smuzhiyun }
985*4882a593Smuzhiyun
986*4882a593Smuzhiyun /**
987*4882a593Smuzhiyun * kbasep_vinstr_fill_dump_buffer - copy accumulated counters to empty kernel
988*4882a593Smuzhiyun * buffer
989*4882a593Smuzhiyun * @cli: requesting client
990*4882a593Smuzhiyun * @timestamp: timestamp when counters were collected
991*4882a593Smuzhiyun * @event_id: id of event that caused triggered counters collection
992*4882a593Smuzhiyun *
993*4882a593Smuzhiyun * Return: zero on success
994*4882a593Smuzhiyun */
kbasep_vinstr_fill_dump_buffer(struct kbase_vinstr_client * cli,u64 timestamp,enum base_hwcnt_reader_event event_id)995*4882a593Smuzhiyun static int kbasep_vinstr_fill_dump_buffer(
996*4882a593Smuzhiyun struct kbase_vinstr_client *cli, u64 timestamp,
997*4882a593Smuzhiyun enum base_hwcnt_reader_event event_id)
998*4882a593Smuzhiyun {
999*4882a593Smuzhiyun unsigned int write_idx = atomic_read(&cli->write_idx);
1000*4882a593Smuzhiyun unsigned int read_idx = atomic_read(&cli->read_idx);
1001*4882a593Smuzhiyun
1002*4882a593Smuzhiyun struct kbase_hwcnt_reader_metadata *meta;
1003*4882a593Smuzhiyun void *buffer;
1004*4882a593Smuzhiyun
1005*4882a593Smuzhiyun /* Check if there is a place to copy HWC block into. */
1006*4882a593Smuzhiyun if (write_idx - read_idx == cli->buffer_count)
1007*4882a593Smuzhiyun return -1;
1008*4882a593Smuzhiyun write_idx %= cli->buffer_count;
1009*4882a593Smuzhiyun
1010*4882a593Smuzhiyun /* Fill in dump buffer and its metadata. */
1011*4882a593Smuzhiyun buffer = &cli->dump_buffers[write_idx * cli->dump_size];
1012*4882a593Smuzhiyun meta = &cli->dump_buffers_meta[write_idx];
1013*4882a593Smuzhiyun meta->timestamp = timestamp;
1014*4882a593Smuzhiyun meta->event_id = event_id;
1015*4882a593Smuzhiyun meta->buffer_idx = write_idx;
1016*4882a593Smuzhiyun memcpy(buffer, cli->accum_buffer, cli->dump_size);
1017*4882a593Smuzhiyun return 0;
1018*4882a593Smuzhiyun }
1019*4882a593Smuzhiyun
1020*4882a593Smuzhiyun /**
1021*4882a593Smuzhiyun * kbasep_vinstr_fill_dump_buffer_legacy - copy accumulated counters to buffer
1022*4882a593Smuzhiyun * allocated in userspace
1023*4882a593Smuzhiyun * @cli: requesting client
1024*4882a593Smuzhiyun *
1025*4882a593Smuzhiyun * Return: zero on success
1026*4882a593Smuzhiyun *
1027*4882a593Smuzhiyun * This is part of legacy ioctl interface.
1028*4882a593Smuzhiyun */
kbasep_vinstr_fill_dump_buffer_legacy(struct kbase_vinstr_client * cli)1029*4882a593Smuzhiyun static int kbasep_vinstr_fill_dump_buffer_legacy(
1030*4882a593Smuzhiyun struct kbase_vinstr_client *cli)
1031*4882a593Smuzhiyun {
1032*4882a593Smuzhiyun void __user *buffer = cli->legacy_buffer;
1033*4882a593Smuzhiyun int rcode;
1034*4882a593Smuzhiyun
1035*4882a593Smuzhiyun /* Copy data to user buffer. */
1036*4882a593Smuzhiyun rcode = copy_to_user(buffer, cli->accum_buffer, cli->dump_size);
1037*4882a593Smuzhiyun if (rcode)
1038*4882a593Smuzhiyun pr_warn("error while copying buffer to user\n");
1039*4882a593Smuzhiyun return rcode;
1040*4882a593Smuzhiyun }
1041*4882a593Smuzhiyun
1042*4882a593Smuzhiyun /**
1043*4882a593Smuzhiyun * kbasep_vinstr_fill_dump_buffer_kernel - copy accumulated counters to buffer
1044*4882a593Smuzhiyun * allocated in kernel space
1045*4882a593Smuzhiyun * @cli: requesting client
1046*4882a593Smuzhiyun *
1047*4882a593Smuzhiyun * Return: zero on success
1048*4882a593Smuzhiyun *
1049*4882a593Smuzhiyun * This is part of the kernel client interface.
1050*4882a593Smuzhiyun */
kbasep_vinstr_fill_dump_buffer_kernel(struct kbase_vinstr_client * cli)1051*4882a593Smuzhiyun static int kbasep_vinstr_fill_dump_buffer_kernel(
1052*4882a593Smuzhiyun struct kbase_vinstr_client *cli)
1053*4882a593Smuzhiyun {
1054*4882a593Smuzhiyun memcpy(cli->kernel_buffer, cli->accum_buffer, cli->dump_size);
1055*4882a593Smuzhiyun
1056*4882a593Smuzhiyun return 0;
1057*4882a593Smuzhiyun }
1058*4882a593Smuzhiyun
1059*4882a593Smuzhiyun /**
1060*4882a593Smuzhiyun * kbasep_vinstr_reprogram - reprogram hwcnt set collected by inst
1061*4882a593Smuzhiyun * @vinstr_ctx: vinstr context
1062*4882a593Smuzhiyun */
kbasep_vinstr_reprogram(struct kbase_vinstr_context * vinstr_ctx)1063*4882a593Smuzhiyun static void kbasep_vinstr_reprogram(
1064*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx)
1065*4882a593Smuzhiyun {
1066*4882a593Smuzhiyun unsigned long flags;
1067*4882a593Smuzhiyun bool suspended = false;
1068*4882a593Smuzhiyun
1069*4882a593Smuzhiyun /* Don't enable hardware counters if vinstr is suspended. */
1070*4882a593Smuzhiyun spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
1071*4882a593Smuzhiyun if (VINSTR_IDLE != vinstr_ctx->state)
1072*4882a593Smuzhiyun suspended = true;
1073*4882a593Smuzhiyun spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
1074*4882a593Smuzhiyun if (suspended)
1075*4882a593Smuzhiyun return;
1076*4882a593Smuzhiyun
1077*4882a593Smuzhiyun /* Change to suspended state is done while holding vinstr context
1078*4882a593Smuzhiyun * lock. Below code will then no re-enable the instrumentation. */
1079*4882a593Smuzhiyun
1080*4882a593Smuzhiyun if (vinstr_ctx->reprogram) {
1081*4882a593Smuzhiyun struct kbase_vinstr_client *iter;
1082*4882a593Smuzhiyun
1083*4882a593Smuzhiyun if (!reprogram_hwcnt(vinstr_ctx)) {
1084*4882a593Smuzhiyun vinstr_ctx->reprogram = false;
1085*4882a593Smuzhiyun list_for_each_entry(
1086*4882a593Smuzhiyun iter,
1087*4882a593Smuzhiyun &vinstr_ctx->idle_clients,
1088*4882a593Smuzhiyun list)
1089*4882a593Smuzhiyun iter->pending = false;
1090*4882a593Smuzhiyun list_for_each_entry(
1091*4882a593Smuzhiyun iter,
1092*4882a593Smuzhiyun &vinstr_ctx->waiting_clients,
1093*4882a593Smuzhiyun list)
1094*4882a593Smuzhiyun iter->pending = false;
1095*4882a593Smuzhiyun }
1096*4882a593Smuzhiyun }
1097*4882a593Smuzhiyun }
1098*4882a593Smuzhiyun
1099*4882a593Smuzhiyun /**
1100*4882a593Smuzhiyun * kbasep_vinstr_update_client - copy accumulated counters to user readable
1101*4882a593Smuzhiyun * buffer and notify the user
1102*4882a593Smuzhiyun * @cli: requesting client
1103*4882a593Smuzhiyun * @timestamp: timestamp when counters were collected
1104*4882a593Smuzhiyun * @event_id: id of event that caused triggered counters collection
1105*4882a593Smuzhiyun *
1106*4882a593Smuzhiyun * Return: zero on success
1107*4882a593Smuzhiyun */
kbasep_vinstr_update_client(struct kbase_vinstr_client * cli,u64 timestamp,enum base_hwcnt_reader_event event_id)1108*4882a593Smuzhiyun static int kbasep_vinstr_update_client(
1109*4882a593Smuzhiyun struct kbase_vinstr_client *cli, u64 timestamp,
1110*4882a593Smuzhiyun enum base_hwcnt_reader_event event_id)
1111*4882a593Smuzhiyun {
1112*4882a593Smuzhiyun int rcode = 0;
1113*4882a593Smuzhiyun
1114*4882a593Smuzhiyun /* Copy collected counters to user readable buffer. */
1115*4882a593Smuzhiyun if (cli->buffer_count)
1116*4882a593Smuzhiyun rcode = kbasep_vinstr_fill_dump_buffer(
1117*4882a593Smuzhiyun cli, timestamp, event_id);
1118*4882a593Smuzhiyun else if (cli->kernel_buffer)
1119*4882a593Smuzhiyun rcode = kbasep_vinstr_fill_dump_buffer_kernel(cli);
1120*4882a593Smuzhiyun else
1121*4882a593Smuzhiyun rcode = kbasep_vinstr_fill_dump_buffer_legacy(cli);
1122*4882a593Smuzhiyun
1123*4882a593Smuzhiyun if (rcode)
1124*4882a593Smuzhiyun goto exit;
1125*4882a593Smuzhiyun
1126*4882a593Smuzhiyun
1127*4882a593Smuzhiyun /* Notify client. Make sure all changes to memory are visible. */
1128*4882a593Smuzhiyun wmb();
1129*4882a593Smuzhiyun atomic_inc(&cli->write_idx);
1130*4882a593Smuzhiyun wake_up_interruptible(&cli->waitq);
1131*4882a593Smuzhiyun
1132*4882a593Smuzhiyun /* Prepare for next request. */
1133*4882a593Smuzhiyun memset(cli->accum_buffer, 0, cli->dump_size);
1134*4882a593Smuzhiyun
1135*4882a593Smuzhiyun exit:
1136*4882a593Smuzhiyun return rcode;
1137*4882a593Smuzhiyun }
1138*4882a593Smuzhiyun
1139*4882a593Smuzhiyun /**
1140*4882a593Smuzhiyun * kbasep_vinstr_wake_up_callback - vinstr wake up timer wake up function
1141*4882a593Smuzhiyun *
1142*4882a593Smuzhiyun * @hrtimer: high resolution timer
1143*4882a593Smuzhiyun *
1144*4882a593Smuzhiyun * Return: High resolution timer restart enum.
1145*4882a593Smuzhiyun */
kbasep_vinstr_wake_up_callback(struct hrtimer * hrtimer)1146*4882a593Smuzhiyun static enum hrtimer_restart kbasep_vinstr_wake_up_callback(
1147*4882a593Smuzhiyun struct hrtimer *hrtimer)
1148*4882a593Smuzhiyun {
1149*4882a593Smuzhiyun struct kbasep_vinstr_wake_up_timer *timer =
1150*4882a593Smuzhiyun container_of(
1151*4882a593Smuzhiyun hrtimer,
1152*4882a593Smuzhiyun struct kbasep_vinstr_wake_up_timer,
1153*4882a593Smuzhiyun hrtimer);
1154*4882a593Smuzhiyun
1155*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(timer);
1156*4882a593Smuzhiyun
1157*4882a593Smuzhiyun atomic_set(&timer->vinstr_ctx->request_pending, 1);
1158*4882a593Smuzhiyun wake_up_all(&timer->vinstr_ctx->waitq);
1159*4882a593Smuzhiyun
1160*4882a593Smuzhiyun return HRTIMER_NORESTART;
1161*4882a593Smuzhiyun }
1162*4882a593Smuzhiyun
1163*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_OBJECT_TIMERS
1164*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0))
1165*4882a593Smuzhiyun /**
1166*4882a593Smuzhiyun * kbase_destroy_hrtimer_on_stack - kernel's destroy_hrtimer_on_stack(),
1167*4882a593Smuzhiyun * rewritten
1168*4882a593Smuzhiyun *
1169*4882a593Smuzhiyun * @timer: high resolution timer
1170*4882a593Smuzhiyun *
1171*4882a593Smuzhiyun * destroy_hrtimer_on_stack() was exported only for 4.7.0 kernel so for
1172*4882a593Smuzhiyun * earlier kernel versions it is not possible to call it explicitly.
1173*4882a593Smuzhiyun * Since this function must accompany hrtimer_init_on_stack(), which
1174*4882a593Smuzhiyun * has to be used for hrtimer initialization if CONFIG_DEBUG_OBJECT_TIMERS
1175*4882a593Smuzhiyun * is defined in order to avoid the warning about object on stack not being
1176*4882a593Smuzhiyun * annotated, we rewrite it here to be used for earlier kernel versions.
1177*4882a593Smuzhiyun */
kbase_destroy_hrtimer_on_stack(struct hrtimer * timer)1178*4882a593Smuzhiyun static void kbase_destroy_hrtimer_on_stack(struct hrtimer *timer)
1179*4882a593Smuzhiyun {
1180*4882a593Smuzhiyun debug_object_free(timer, &hrtimer_debug_descr);
1181*4882a593Smuzhiyun }
1182*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) */
1183*4882a593Smuzhiyun #endif /* CONFIG_DEBUG_OBJECT_TIMERS */
1184*4882a593Smuzhiyun
1185*4882a593Smuzhiyun /**
1186*4882a593Smuzhiyun * kbasep_vinstr_service_task - HWC dumping service thread
1187*4882a593Smuzhiyun *
1188*4882a593Smuzhiyun * @data: Pointer to vinstr context structure.
1189*4882a593Smuzhiyun *
1190*4882a593Smuzhiyun * Return: Always returns zero.
1191*4882a593Smuzhiyun */
kbasep_vinstr_service_task(void * data)1192*4882a593Smuzhiyun static int kbasep_vinstr_service_task(void *data)
1193*4882a593Smuzhiyun {
1194*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx = data;
1195*4882a593Smuzhiyun struct kbasep_vinstr_wake_up_timer timer;
1196*4882a593Smuzhiyun
1197*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
1198*4882a593Smuzhiyun
1199*4882a593Smuzhiyun hrtimer_init_on_stack(&timer.hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
1200*4882a593Smuzhiyun
1201*4882a593Smuzhiyun timer.hrtimer.function = kbasep_vinstr_wake_up_callback;
1202*4882a593Smuzhiyun timer.vinstr_ctx = vinstr_ctx;
1203*4882a593Smuzhiyun
1204*4882a593Smuzhiyun while (!kthread_should_stop()) {
1205*4882a593Smuzhiyun struct kbase_vinstr_client *cli = NULL;
1206*4882a593Smuzhiyun struct kbase_vinstr_client *tmp;
1207*4882a593Smuzhiyun int rcode;
1208*4882a593Smuzhiyun
1209*4882a593Smuzhiyun u64 timestamp = kbasep_vinstr_get_timestamp();
1210*4882a593Smuzhiyun u64 dump_time = 0;
1211*4882a593Smuzhiyun struct list_head expired_requests;
1212*4882a593Smuzhiyun
1213*4882a593Smuzhiyun /* Hold lock while performing operations on lists of clients. */
1214*4882a593Smuzhiyun mutex_lock(&vinstr_ctx->lock);
1215*4882a593Smuzhiyun
1216*4882a593Smuzhiyun /* Closing thread must not interact with client requests. */
1217*4882a593Smuzhiyun if (current == vinstr_ctx->thread) {
1218*4882a593Smuzhiyun atomic_set(&vinstr_ctx->request_pending, 0);
1219*4882a593Smuzhiyun
1220*4882a593Smuzhiyun if (!list_empty(&vinstr_ctx->waiting_clients)) {
1221*4882a593Smuzhiyun cli = list_first_entry(
1222*4882a593Smuzhiyun &vinstr_ctx->waiting_clients,
1223*4882a593Smuzhiyun struct kbase_vinstr_client,
1224*4882a593Smuzhiyun list);
1225*4882a593Smuzhiyun dump_time = cli->dump_time;
1226*4882a593Smuzhiyun }
1227*4882a593Smuzhiyun }
1228*4882a593Smuzhiyun
1229*4882a593Smuzhiyun if (!cli || ((s64)timestamp - (s64)dump_time < 0ll)) {
1230*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
1231*4882a593Smuzhiyun
1232*4882a593Smuzhiyun /* Sleep until next dumping event or service request. */
1233*4882a593Smuzhiyun if (cli) {
1234*4882a593Smuzhiyun u64 diff = dump_time - timestamp;
1235*4882a593Smuzhiyun
1236*4882a593Smuzhiyun hrtimer_start(
1237*4882a593Smuzhiyun &timer.hrtimer,
1238*4882a593Smuzhiyun ns_to_ktime(diff),
1239*4882a593Smuzhiyun HRTIMER_MODE_REL);
1240*4882a593Smuzhiyun }
1241*4882a593Smuzhiyun wait_event(
1242*4882a593Smuzhiyun vinstr_ctx->waitq,
1243*4882a593Smuzhiyun atomic_read(
1244*4882a593Smuzhiyun &vinstr_ctx->request_pending) ||
1245*4882a593Smuzhiyun kthread_should_stop());
1246*4882a593Smuzhiyun hrtimer_cancel(&timer.hrtimer);
1247*4882a593Smuzhiyun continue;
1248*4882a593Smuzhiyun }
1249*4882a593Smuzhiyun
1250*4882a593Smuzhiyun rcode = kbasep_vinstr_collect_and_accumulate(vinstr_ctx,
1251*4882a593Smuzhiyun ×tamp);
1252*4882a593Smuzhiyun
1253*4882a593Smuzhiyun INIT_LIST_HEAD(&expired_requests);
1254*4882a593Smuzhiyun
1255*4882a593Smuzhiyun /* Find all expired requests. */
1256*4882a593Smuzhiyun list_for_each_entry_safe(
1257*4882a593Smuzhiyun cli,
1258*4882a593Smuzhiyun tmp,
1259*4882a593Smuzhiyun &vinstr_ctx->waiting_clients,
1260*4882a593Smuzhiyun list) {
1261*4882a593Smuzhiyun s64 tdiff =
1262*4882a593Smuzhiyun (s64)(timestamp + DUMPING_RESOLUTION) -
1263*4882a593Smuzhiyun (s64)cli->dump_time;
1264*4882a593Smuzhiyun if (tdiff >= 0ll) {
1265*4882a593Smuzhiyun list_del(&cli->list);
1266*4882a593Smuzhiyun list_add(&cli->list, &expired_requests);
1267*4882a593Smuzhiyun } else {
1268*4882a593Smuzhiyun break;
1269*4882a593Smuzhiyun }
1270*4882a593Smuzhiyun }
1271*4882a593Smuzhiyun
1272*4882a593Smuzhiyun /* Fill data for each request found. */
1273*4882a593Smuzhiyun list_for_each_entry_safe(cli, tmp, &expired_requests, list) {
1274*4882a593Smuzhiyun /* Ensure that legacy buffer will not be used from
1275*4882a593Smuzhiyun * this kthread context. */
1276*4882a593Smuzhiyun BUG_ON(0 == cli->buffer_count);
1277*4882a593Smuzhiyun /* Expect only periodically sampled clients. */
1278*4882a593Smuzhiyun BUG_ON(0 == cli->dump_interval);
1279*4882a593Smuzhiyun
1280*4882a593Smuzhiyun if (!rcode)
1281*4882a593Smuzhiyun kbasep_vinstr_update_client(
1282*4882a593Smuzhiyun cli,
1283*4882a593Smuzhiyun timestamp,
1284*4882a593Smuzhiyun BASE_HWCNT_READER_EVENT_PERIODIC);
1285*4882a593Smuzhiyun
1286*4882a593Smuzhiyun /* Set new dumping time. Drop missed probing times. */
1287*4882a593Smuzhiyun do {
1288*4882a593Smuzhiyun cli->dump_time += cli->dump_interval;
1289*4882a593Smuzhiyun } while (cli->dump_time < timestamp);
1290*4882a593Smuzhiyun
1291*4882a593Smuzhiyun list_del(&cli->list);
1292*4882a593Smuzhiyun kbasep_vinstr_add_dump_request(
1293*4882a593Smuzhiyun cli,
1294*4882a593Smuzhiyun &vinstr_ctx->waiting_clients);
1295*4882a593Smuzhiyun }
1296*4882a593Smuzhiyun
1297*4882a593Smuzhiyun /* Reprogram counters set if required. */
1298*4882a593Smuzhiyun kbasep_vinstr_reprogram(vinstr_ctx);
1299*4882a593Smuzhiyun
1300*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
1301*4882a593Smuzhiyun }
1302*4882a593Smuzhiyun
1303*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_OBJECTS_TIMERS
1304*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0))
1305*4882a593Smuzhiyun kbase_destroy_hrtimer_on_stack(&timer.hrtimer);
1306*4882a593Smuzhiyun #else
1307*4882a593Smuzhiyun destroy_hrtimer_on_stack(&timer.hrtimer);
1308*4882a593Smuzhiyun #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)) */
1309*4882a593Smuzhiyun #endif /* CONFIG_DEBUG_OBJECTS_TIMERS */
1310*4882a593Smuzhiyun
1311*4882a593Smuzhiyun return 0;
1312*4882a593Smuzhiyun }
1313*4882a593Smuzhiyun
1314*4882a593Smuzhiyun /*****************************************************************************/
1315*4882a593Smuzhiyun
1316*4882a593Smuzhiyun /**
1317*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_buffer_ready - check if client has ready buffers
1318*4882a593Smuzhiyun * @cli: pointer to vinstr client structure
1319*4882a593Smuzhiyun *
1320*4882a593Smuzhiyun * Return: non-zero if client has at least one dumping buffer filled that was
1321*4882a593Smuzhiyun * not notified to user yet
1322*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_buffer_ready(struct kbase_vinstr_client * cli)1323*4882a593Smuzhiyun static int kbasep_vinstr_hwcnt_reader_buffer_ready(
1324*4882a593Smuzhiyun struct kbase_vinstr_client *cli)
1325*4882a593Smuzhiyun {
1326*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(cli);
1327*4882a593Smuzhiyun return atomic_read(&cli->write_idx) != atomic_read(&cli->meta_idx);
1328*4882a593Smuzhiyun }
1329*4882a593Smuzhiyun
1330*4882a593Smuzhiyun /**
1331*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_ioctl_get_buffer - hwcnt reader's ioctl command
1332*4882a593Smuzhiyun * @cli: pointer to vinstr client structure
1333*4882a593Smuzhiyun * @buffer: pointer to userspace buffer
1334*4882a593Smuzhiyun * @size: size of buffer
1335*4882a593Smuzhiyun *
1336*4882a593Smuzhiyun * Return: zero on success
1337*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_ioctl_get_buffer(struct kbase_vinstr_client * cli,void __user * buffer,size_t size)1338*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_get_buffer(
1339*4882a593Smuzhiyun struct kbase_vinstr_client *cli, void __user *buffer,
1340*4882a593Smuzhiyun size_t size)
1341*4882a593Smuzhiyun {
1342*4882a593Smuzhiyun unsigned int meta_idx = atomic_read(&cli->meta_idx);
1343*4882a593Smuzhiyun unsigned int idx = meta_idx % cli->buffer_count;
1344*4882a593Smuzhiyun
1345*4882a593Smuzhiyun struct kbase_hwcnt_reader_metadata *meta = &cli->dump_buffers_meta[idx];
1346*4882a593Smuzhiyun
1347*4882a593Smuzhiyun /* Metadata sanity check. */
1348*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(idx == meta->buffer_idx);
1349*4882a593Smuzhiyun
1350*4882a593Smuzhiyun if (sizeof(struct kbase_hwcnt_reader_metadata) != size)
1351*4882a593Smuzhiyun return -EINVAL;
1352*4882a593Smuzhiyun
1353*4882a593Smuzhiyun /* Check if there is any buffer available. */
1354*4882a593Smuzhiyun if (atomic_read(&cli->write_idx) == meta_idx)
1355*4882a593Smuzhiyun return -EAGAIN;
1356*4882a593Smuzhiyun
1357*4882a593Smuzhiyun /* Check if previously taken buffer was put back. */
1358*4882a593Smuzhiyun if (atomic_read(&cli->read_idx) != meta_idx)
1359*4882a593Smuzhiyun return -EBUSY;
1360*4882a593Smuzhiyun
1361*4882a593Smuzhiyun /* Copy next available buffer's metadata to user. */
1362*4882a593Smuzhiyun if (copy_to_user(buffer, meta, size))
1363*4882a593Smuzhiyun return -EFAULT;
1364*4882a593Smuzhiyun
1365*4882a593Smuzhiyun atomic_inc(&cli->meta_idx);
1366*4882a593Smuzhiyun
1367*4882a593Smuzhiyun return 0;
1368*4882a593Smuzhiyun }
1369*4882a593Smuzhiyun
1370*4882a593Smuzhiyun /**
1371*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_ioctl_put_buffer - hwcnt reader's ioctl command
1372*4882a593Smuzhiyun * @cli: pointer to vinstr client structure
1373*4882a593Smuzhiyun * @buffer: pointer to userspace buffer
1374*4882a593Smuzhiyun * @size: size of buffer
1375*4882a593Smuzhiyun *
1376*4882a593Smuzhiyun * Return: zero on success
1377*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_ioctl_put_buffer(struct kbase_vinstr_client * cli,void __user * buffer,size_t size)1378*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_put_buffer(
1379*4882a593Smuzhiyun struct kbase_vinstr_client *cli, void __user *buffer,
1380*4882a593Smuzhiyun size_t size)
1381*4882a593Smuzhiyun {
1382*4882a593Smuzhiyun unsigned int read_idx = atomic_read(&cli->read_idx);
1383*4882a593Smuzhiyun unsigned int idx = read_idx % cli->buffer_count;
1384*4882a593Smuzhiyun
1385*4882a593Smuzhiyun struct kbase_hwcnt_reader_metadata meta;
1386*4882a593Smuzhiyun
1387*4882a593Smuzhiyun if (sizeof(struct kbase_hwcnt_reader_metadata) != size)
1388*4882a593Smuzhiyun return -EINVAL;
1389*4882a593Smuzhiyun
1390*4882a593Smuzhiyun /* Check if any buffer was taken. */
1391*4882a593Smuzhiyun if (atomic_read(&cli->meta_idx) == read_idx)
1392*4882a593Smuzhiyun return -EPERM;
1393*4882a593Smuzhiyun
1394*4882a593Smuzhiyun /* Check if correct buffer is put back. */
1395*4882a593Smuzhiyun if (copy_from_user(&meta, buffer, size))
1396*4882a593Smuzhiyun return -EFAULT;
1397*4882a593Smuzhiyun if (idx != meta.buffer_idx)
1398*4882a593Smuzhiyun return -EINVAL;
1399*4882a593Smuzhiyun
1400*4882a593Smuzhiyun atomic_inc(&cli->read_idx);
1401*4882a593Smuzhiyun
1402*4882a593Smuzhiyun return 0;
1403*4882a593Smuzhiyun }
1404*4882a593Smuzhiyun
1405*4882a593Smuzhiyun /**
1406*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_ioctl_set_interval - hwcnt reader's ioctl command
1407*4882a593Smuzhiyun * @cli: pointer to vinstr client structure
1408*4882a593Smuzhiyun * @interval: periodic dumping interval (disable periodic dumping if zero)
1409*4882a593Smuzhiyun *
1410*4882a593Smuzhiyun * Return: zero on success
1411*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_ioctl_set_interval(struct kbase_vinstr_client * cli,u32 interval)1412*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_set_interval(
1413*4882a593Smuzhiyun struct kbase_vinstr_client *cli, u32 interval)
1414*4882a593Smuzhiyun {
1415*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx = cli->vinstr_ctx;
1416*4882a593Smuzhiyun
1417*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
1418*4882a593Smuzhiyun
1419*4882a593Smuzhiyun mutex_lock(&vinstr_ctx->lock);
1420*4882a593Smuzhiyun
1421*4882a593Smuzhiyun list_del(&cli->list);
1422*4882a593Smuzhiyun
1423*4882a593Smuzhiyun cli->dump_interval = interval;
1424*4882a593Smuzhiyun
1425*4882a593Smuzhiyun /* If interval is non-zero, enable periodic dumping for this client. */
1426*4882a593Smuzhiyun if (cli->dump_interval) {
1427*4882a593Smuzhiyun if (DUMPING_RESOLUTION > cli->dump_interval)
1428*4882a593Smuzhiyun cli->dump_interval = DUMPING_RESOLUTION;
1429*4882a593Smuzhiyun cli->dump_time =
1430*4882a593Smuzhiyun kbasep_vinstr_get_timestamp() + cli->dump_interval;
1431*4882a593Smuzhiyun
1432*4882a593Smuzhiyun kbasep_vinstr_add_dump_request(
1433*4882a593Smuzhiyun cli, &vinstr_ctx->waiting_clients);
1434*4882a593Smuzhiyun
1435*4882a593Smuzhiyun atomic_set(&vinstr_ctx->request_pending, 1);
1436*4882a593Smuzhiyun wake_up_all(&vinstr_ctx->waitq);
1437*4882a593Smuzhiyun } else {
1438*4882a593Smuzhiyun list_add(&cli->list, &vinstr_ctx->idle_clients);
1439*4882a593Smuzhiyun }
1440*4882a593Smuzhiyun
1441*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
1442*4882a593Smuzhiyun
1443*4882a593Smuzhiyun return 0;
1444*4882a593Smuzhiyun }
1445*4882a593Smuzhiyun
1446*4882a593Smuzhiyun /**
1447*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_event_mask - return event mask for event id
1448*4882a593Smuzhiyun * @event_id: id of event
1449*4882a593Smuzhiyun * Return: event_mask or zero if event is not supported or maskable
1450*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_event_mask(enum base_hwcnt_reader_event event_id)1451*4882a593Smuzhiyun static u32 kbasep_vinstr_hwcnt_reader_event_mask(
1452*4882a593Smuzhiyun enum base_hwcnt_reader_event event_id)
1453*4882a593Smuzhiyun {
1454*4882a593Smuzhiyun u32 event_mask = 0;
1455*4882a593Smuzhiyun
1456*4882a593Smuzhiyun switch (event_id) {
1457*4882a593Smuzhiyun case BASE_HWCNT_READER_EVENT_PREJOB:
1458*4882a593Smuzhiyun case BASE_HWCNT_READER_EVENT_POSTJOB:
1459*4882a593Smuzhiyun /* These event are maskable. */
1460*4882a593Smuzhiyun event_mask = (1 << event_id);
1461*4882a593Smuzhiyun break;
1462*4882a593Smuzhiyun
1463*4882a593Smuzhiyun case BASE_HWCNT_READER_EVENT_MANUAL:
1464*4882a593Smuzhiyun case BASE_HWCNT_READER_EVENT_PERIODIC:
1465*4882a593Smuzhiyun /* These event are non-maskable. */
1466*4882a593Smuzhiyun default:
1467*4882a593Smuzhiyun /* These event are not supported. */
1468*4882a593Smuzhiyun break;
1469*4882a593Smuzhiyun }
1470*4882a593Smuzhiyun
1471*4882a593Smuzhiyun return event_mask;
1472*4882a593Smuzhiyun }
1473*4882a593Smuzhiyun
1474*4882a593Smuzhiyun /**
1475*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_ioctl_enable_event - hwcnt reader's ioctl command
1476*4882a593Smuzhiyun * @cli: pointer to vinstr client structure
1477*4882a593Smuzhiyun * @event_id: id of event to enable
1478*4882a593Smuzhiyun *
1479*4882a593Smuzhiyun * Return: zero on success
1480*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_ioctl_enable_event(struct kbase_vinstr_client * cli,enum base_hwcnt_reader_event event_id)1481*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_enable_event(
1482*4882a593Smuzhiyun struct kbase_vinstr_client *cli,
1483*4882a593Smuzhiyun enum base_hwcnt_reader_event event_id)
1484*4882a593Smuzhiyun {
1485*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx = cli->vinstr_ctx;
1486*4882a593Smuzhiyun u32 event_mask;
1487*4882a593Smuzhiyun
1488*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
1489*4882a593Smuzhiyun
1490*4882a593Smuzhiyun event_mask = kbasep_vinstr_hwcnt_reader_event_mask(event_id);
1491*4882a593Smuzhiyun if (!event_mask)
1492*4882a593Smuzhiyun return -EINVAL;
1493*4882a593Smuzhiyun
1494*4882a593Smuzhiyun mutex_lock(&vinstr_ctx->lock);
1495*4882a593Smuzhiyun cli->event_mask |= event_mask;
1496*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
1497*4882a593Smuzhiyun
1498*4882a593Smuzhiyun return 0;
1499*4882a593Smuzhiyun }
1500*4882a593Smuzhiyun
1501*4882a593Smuzhiyun /**
1502*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_ioctl_disable_event - hwcnt reader's ioctl command
1503*4882a593Smuzhiyun * @cli: pointer to vinstr client structure
1504*4882a593Smuzhiyun * @event_id: id of event to disable
1505*4882a593Smuzhiyun *
1506*4882a593Smuzhiyun * Return: zero on success
1507*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_ioctl_disable_event(struct kbase_vinstr_client * cli,enum base_hwcnt_reader_event event_id)1508*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_disable_event(
1509*4882a593Smuzhiyun struct kbase_vinstr_client *cli,
1510*4882a593Smuzhiyun enum base_hwcnt_reader_event event_id)
1511*4882a593Smuzhiyun {
1512*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx = cli->vinstr_ctx;
1513*4882a593Smuzhiyun u32 event_mask;
1514*4882a593Smuzhiyun
1515*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
1516*4882a593Smuzhiyun
1517*4882a593Smuzhiyun event_mask = kbasep_vinstr_hwcnt_reader_event_mask(event_id);
1518*4882a593Smuzhiyun if (!event_mask)
1519*4882a593Smuzhiyun return -EINVAL;
1520*4882a593Smuzhiyun
1521*4882a593Smuzhiyun mutex_lock(&vinstr_ctx->lock);
1522*4882a593Smuzhiyun cli->event_mask &= ~event_mask;
1523*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
1524*4882a593Smuzhiyun
1525*4882a593Smuzhiyun return 0;
1526*4882a593Smuzhiyun }
1527*4882a593Smuzhiyun
1528*4882a593Smuzhiyun /**
1529*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_ioctl_get_hwver - hwcnt reader's ioctl command
1530*4882a593Smuzhiyun * @cli: pointer to vinstr client structure
1531*4882a593Smuzhiyun * @hwver: pointer to user buffer where hw version will be stored
1532*4882a593Smuzhiyun *
1533*4882a593Smuzhiyun * Return: zero on success
1534*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_ioctl_get_hwver(struct kbase_vinstr_client * cli,u32 __user * hwver)1535*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_get_hwver(
1536*4882a593Smuzhiyun struct kbase_vinstr_client *cli, u32 __user *hwver)
1537*4882a593Smuzhiyun {
1538*4882a593Smuzhiyun #ifndef CONFIG_MALI_NO_MALI
1539*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx = cli->vinstr_ctx;
1540*4882a593Smuzhiyun #endif
1541*4882a593Smuzhiyun
1542*4882a593Smuzhiyun u32 ver = 5;
1543*4882a593Smuzhiyun
1544*4882a593Smuzhiyun #ifndef CONFIG_MALI_NO_MALI
1545*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
1546*4882a593Smuzhiyun if (kbase_hw_has_feature(vinstr_ctx->kbdev, BASE_HW_FEATURE_V4))
1547*4882a593Smuzhiyun ver = 4;
1548*4882a593Smuzhiyun #endif
1549*4882a593Smuzhiyun
1550*4882a593Smuzhiyun return put_user(ver, hwver);
1551*4882a593Smuzhiyun }
1552*4882a593Smuzhiyun
1553*4882a593Smuzhiyun /**
1554*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_ioctl - hwcnt reader's ioctl
1555*4882a593Smuzhiyun * @filp: pointer to file structure
1556*4882a593Smuzhiyun * @cmd: user command
1557*4882a593Smuzhiyun * @arg: command's argument
1558*4882a593Smuzhiyun *
1559*4882a593Smuzhiyun * Return: zero on success
1560*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)1561*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl(struct file *filp,
1562*4882a593Smuzhiyun unsigned int cmd, unsigned long arg)
1563*4882a593Smuzhiyun {
1564*4882a593Smuzhiyun long rcode = 0;
1565*4882a593Smuzhiyun struct kbase_vinstr_client *cli;
1566*4882a593Smuzhiyun
1567*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(filp);
1568*4882a593Smuzhiyun
1569*4882a593Smuzhiyun cli = filp->private_data;
1570*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(cli);
1571*4882a593Smuzhiyun
1572*4882a593Smuzhiyun if (unlikely(KBASE_HWCNT_READER != _IOC_TYPE(cmd)))
1573*4882a593Smuzhiyun return -EINVAL;
1574*4882a593Smuzhiyun
1575*4882a593Smuzhiyun switch (cmd) {
1576*4882a593Smuzhiyun case KBASE_HWCNT_READER_GET_API_VERSION:
1577*4882a593Smuzhiyun rcode = put_user(HWCNT_READER_API, (u32 __user *)arg);
1578*4882a593Smuzhiyun break;
1579*4882a593Smuzhiyun case KBASE_HWCNT_READER_GET_HWVER:
1580*4882a593Smuzhiyun rcode = kbasep_vinstr_hwcnt_reader_ioctl_get_hwver(
1581*4882a593Smuzhiyun cli, (u32 __user *)arg);
1582*4882a593Smuzhiyun break;
1583*4882a593Smuzhiyun case KBASE_HWCNT_READER_GET_BUFFER_SIZE:
1584*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(cli->vinstr_ctx);
1585*4882a593Smuzhiyun rcode = put_user(
1586*4882a593Smuzhiyun (u32)cli->vinstr_ctx->dump_size,
1587*4882a593Smuzhiyun (u32 __user *)arg);
1588*4882a593Smuzhiyun break;
1589*4882a593Smuzhiyun case KBASE_HWCNT_READER_DUMP:
1590*4882a593Smuzhiyun rcode = kbase_vinstr_hwc_dump(
1591*4882a593Smuzhiyun cli, BASE_HWCNT_READER_EVENT_MANUAL);
1592*4882a593Smuzhiyun break;
1593*4882a593Smuzhiyun case KBASE_HWCNT_READER_CLEAR:
1594*4882a593Smuzhiyun rcode = kbase_vinstr_hwc_clear(cli);
1595*4882a593Smuzhiyun break;
1596*4882a593Smuzhiyun case KBASE_HWCNT_READER_GET_BUFFER:
1597*4882a593Smuzhiyun rcode = kbasep_vinstr_hwcnt_reader_ioctl_get_buffer(
1598*4882a593Smuzhiyun cli, (void __user *)arg, _IOC_SIZE(cmd));
1599*4882a593Smuzhiyun break;
1600*4882a593Smuzhiyun case KBASE_HWCNT_READER_PUT_BUFFER:
1601*4882a593Smuzhiyun rcode = kbasep_vinstr_hwcnt_reader_ioctl_put_buffer(
1602*4882a593Smuzhiyun cli, (void __user *)arg, _IOC_SIZE(cmd));
1603*4882a593Smuzhiyun break;
1604*4882a593Smuzhiyun case KBASE_HWCNT_READER_SET_INTERVAL:
1605*4882a593Smuzhiyun rcode = kbasep_vinstr_hwcnt_reader_ioctl_set_interval(
1606*4882a593Smuzhiyun cli, (u32)arg);
1607*4882a593Smuzhiyun break;
1608*4882a593Smuzhiyun case KBASE_HWCNT_READER_ENABLE_EVENT:
1609*4882a593Smuzhiyun rcode = kbasep_vinstr_hwcnt_reader_ioctl_enable_event(
1610*4882a593Smuzhiyun cli, (enum base_hwcnt_reader_event)arg);
1611*4882a593Smuzhiyun break;
1612*4882a593Smuzhiyun case KBASE_HWCNT_READER_DISABLE_EVENT:
1613*4882a593Smuzhiyun rcode = kbasep_vinstr_hwcnt_reader_ioctl_disable_event(
1614*4882a593Smuzhiyun cli, (enum base_hwcnt_reader_event)arg);
1615*4882a593Smuzhiyun break;
1616*4882a593Smuzhiyun default:
1617*4882a593Smuzhiyun rcode = -EINVAL;
1618*4882a593Smuzhiyun break;
1619*4882a593Smuzhiyun }
1620*4882a593Smuzhiyun
1621*4882a593Smuzhiyun return rcode;
1622*4882a593Smuzhiyun }
1623*4882a593Smuzhiyun
1624*4882a593Smuzhiyun /**
1625*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_poll - hwcnt reader's poll
1626*4882a593Smuzhiyun * @filp: pointer to file structure
1627*4882a593Smuzhiyun * @wait: pointer to poll table
1628*4882a593Smuzhiyun * Return: POLLIN if data can be read without blocking, otherwise zero
1629*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_poll(struct file * filp,poll_table * wait)1630*4882a593Smuzhiyun static unsigned int kbasep_vinstr_hwcnt_reader_poll(struct file *filp,
1631*4882a593Smuzhiyun poll_table *wait)
1632*4882a593Smuzhiyun {
1633*4882a593Smuzhiyun struct kbase_vinstr_client *cli;
1634*4882a593Smuzhiyun
1635*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(filp);
1636*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(wait);
1637*4882a593Smuzhiyun
1638*4882a593Smuzhiyun cli = filp->private_data;
1639*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(cli);
1640*4882a593Smuzhiyun
1641*4882a593Smuzhiyun poll_wait(filp, &cli->waitq, wait);
1642*4882a593Smuzhiyun if (kbasep_vinstr_hwcnt_reader_buffer_ready(cli))
1643*4882a593Smuzhiyun return POLLIN;
1644*4882a593Smuzhiyun return 0;
1645*4882a593Smuzhiyun }
1646*4882a593Smuzhiyun
1647*4882a593Smuzhiyun /**
1648*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_mmap - hwcnt reader's mmap
1649*4882a593Smuzhiyun * @filp: pointer to file structure
1650*4882a593Smuzhiyun * @vma: pointer to vma structure
1651*4882a593Smuzhiyun * Return: zero on success
1652*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_mmap(struct file * filp,struct vm_area_struct * vma)1653*4882a593Smuzhiyun static int kbasep_vinstr_hwcnt_reader_mmap(struct file *filp,
1654*4882a593Smuzhiyun struct vm_area_struct *vma)
1655*4882a593Smuzhiyun {
1656*4882a593Smuzhiyun struct kbase_vinstr_client *cli;
1657*4882a593Smuzhiyun unsigned long size, addr, pfn, offset;
1658*4882a593Smuzhiyun unsigned long vm_size = vma->vm_end - vma->vm_start;
1659*4882a593Smuzhiyun
1660*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(filp);
1661*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vma);
1662*4882a593Smuzhiyun
1663*4882a593Smuzhiyun cli = filp->private_data;
1664*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(cli);
1665*4882a593Smuzhiyun
1666*4882a593Smuzhiyun size = cli->buffer_count * cli->dump_size;
1667*4882a593Smuzhiyun
1668*4882a593Smuzhiyun if (vma->vm_pgoff > (size >> PAGE_SHIFT))
1669*4882a593Smuzhiyun return -EINVAL;
1670*4882a593Smuzhiyun
1671*4882a593Smuzhiyun offset = vma->vm_pgoff << PAGE_SHIFT;
1672*4882a593Smuzhiyun if (vm_size > size - offset)
1673*4882a593Smuzhiyun return -EINVAL;
1674*4882a593Smuzhiyun
1675*4882a593Smuzhiyun addr = __pa((unsigned long)cli->dump_buffers + offset);
1676*4882a593Smuzhiyun pfn = addr >> PAGE_SHIFT;
1677*4882a593Smuzhiyun
1678*4882a593Smuzhiyun return remap_pfn_range(
1679*4882a593Smuzhiyun vma,
1680*4882a593Smuzhiyun vma->vm_start,
1681*4882a593Smuzhiyun pfn,
1682*4882a593Smuzhiyun vm_size,
1683*4882a593Smuzhiyun vma->vm_page_prot);
1684*4882a593Smuzhiyun }
1685*4882a593Smuzhiyun
1686*4882a593Smuzhiyun /**
1687*4882a593Smuzhiyun * kbasep_vinstr_hwcnt_reader_release - hwcnt reader's release
1688*4882a593Smuzhiyun * @inode: pointer to inode structure
1689*4882a593Smuzhiyun * @filp: pointer to file structure
1690*4882a593Smuzhiyun * Return always return zero
1691*4882a593Smuzhiyun */
kbasep_vinstr_hwcnt_reader_release(struct inode * inode,struct file * filp)1692*4882a593Smuzhiyun static int kbasep_vinstr_hwcnt_reader_release(struct inode *inode,
1693*4882a593Smuzhiyun struct file *filp)
1694*4882a593Smuzhiyun {
1695*4882a593Smuzhiyun struct kbase_vinstr_client *cli;
1696*4882a593Smuzhiyun
1697*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(inode);
1698*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(filp);
1699*4882a593Smuzhiyun
1700*4882a593Smuzhiyun cli = filp->private_data;
1701*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(cli);
1702*4882a593Smuzhiyun
1703*4882a593Smuzhiyun kbase_vinstr_detach_client(cli);
1704*4882a593Smuzhiyun return 0;
1705*4882a593Smuzhiyun }
1706*4882a593Smuzhiyun
1707*4882a593Smuzhiyun /*****************************************************************************/
1708*4882a593Smuzhiyun
1709*4882a593Smuzhiyun /**
1710*4882a593Smuzhiyun * kbasep_vinstr_kick_scheduler - trigger scheduler cycle
1711*4882a593Smuzhiyun * @kbdev: pointer to kbase device structure
1712*4882a593Smuzhiyun */
kbasep_vinstr_kick_scheduler(struct kbase_device * kbdev)1713*4882a593Smuzhiyun static void kbasep_vinstr_kick_scheduler(struct kbase_device *kbdev)
1714*4882a593Smuzhiyun {
1715*4882a593Smuzhiyun struct kbasep_js_device_data *js_devdata = &kbdev->js_data;
1716*4882a593Smuzhiyun unsigned long flags;
1717*4882a593Smuzhiyun
1718*4882a593Smuzhiyun down(&js_devdata->schedule_sem);
1719*4882a593Smuzhiyun spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
1720*4882a593Smuzhiyun kbase_backend_slot_update(kbdev);
1721*4882a593Smuzhiyun spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
1722*4882a593Smuzhiyun up(&js_devdata->schedule_sem);
1723*4882a593Smuzhiyun }
1724*4882a593Smuzhiyun
1725*4882a593Smuzhiyun /**
1726*4882a593Smuzhiyun * kbasep_vinstr_suspend_worker - worker suspending vinstr module
1727*4882a593Smuzhiyun * @data: pointer to work structure
1728*4882a593Smuzhiyun */
kbasep_vinstr_suspend_worker(struct work_struct * data)1729*4882a593Smuzhiyun static void kbasep_vinstr_suspend_worker(struct work_struct *data)
1730*4882a593Smuzhiyun {
1731*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx;
1732*4882a593Smuzhiyun unsigned long flags;
1733*4882a593Smuzhiyun
1734*4882a593Smuzhiyun vinstr_ctx = container_of(data, struct kbase_vinstr_context,
1735*4882a593Smuzhiyun suspend_work);
1736*4882a593Smuzhiyun
1737*4882a593Smuzhiyun mutex_lock(&vinstr_ctx->lock);
1738*4882a593Smuzhiyun
1739*4882a593Smuzhiyun if (vinstr_ctx->kctx)
1740*4882a593Smuzhiyun disable_hwcnt(vinstr_ctx);
1741*4882a593Smuzhiyun
1742*4882a593Smuzhiyun spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
1743*4882a593Smuzhiyun vinstr_ctx->state = VINSTR_SUSPENDED;
1744*4882a593Smuzhiyun wake_up_all(&vinstr_ctx->suspend_waitq);
1745*4882a593Smuzhiyun spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
1746*4882a593Smuzhiyun
1747*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
1748*4882a593Smuzhiyun
1749*4882a593Smuzhiyun /* Kick GPU scheduler to allow entering protected mode.
1750*4882a593Smuzhiyun * This must happen after vinstr was suspended. */
1751*4882a593Smuzhiyun kbasep_vinstr_kick_scheduler(vinstr_ctx->kbdev);
1752*4882a593Smuzhiyun }
1753*4882a593Smuzhiyun
1754*4882a593Smuzhiyun /**
1755*4882a593Smuzhiyun * kbasep_vinstr_suspend_worker - worker resuming vinstr module
1756*4882a593Smuzhiyun * @data: pointer to work structure
1757*4882a593Smuzhiyun */
kbasep_vinstr_resume_worker(struct work_struct * data)1758*4882a593Smuzhiyun static void kbasep_vinstr_resume_worker(struct work_struct *data)
1759*4882a593Smuzhiyun {
1760*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx;
1761*4882a593Smuzhiyun unsigned long flags;
1762*4882a593Smuzhiyun
1763*4882a593Smuzhiyun vinstr_ctx = container_of(data, struct kbase_vinstr_context,
1764*4882a593Smuzhiyun resume_work);
1765*4882a593Smuzhiyun
1766*4882a593Smuzhiyun mutex_lock(&vinstr_ctx->lock);
1767*4882a593Smuzhiyun
1768*4882a593Smuzhiyun if (vinstr_ctx->kctx)
1769*4882a593Smuzhiyun enable_hwcnt(vinstr_ctx);
1770*4882a593Smuzhiyun
1771*4882a593Smuzhiyun spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
1772*4882a593Smuzhiyun vinstr_ctx->state = VINSTR_IDLE;
1773*4882a593Smuzhiyun wake_up_all(&vinstr_ctx->suspend_waitq);
1774*4882a593Smuzhiyun spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
1775*4882a593Smuzhiyun
1776*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
1777*4882a593Smuzhiyun
1778*4882a593Smuzhiyun /* Kick GPU scheduler to allow entering protected mode.
1779*4882a593Smuzhiyun * Note that scheduler state machine might requested re-entry to
1780*4882a593Smuzhiyun * protected mode before vinstr was resumed.
1781*4882a593Smuzhiyun * This must happen after vinstr was release. */
1782*4882a593Smuzhiyun kbasep_vinstr_kick_scheduler(vinstr_ctx->kbdev);
1783*4882a593Smuzhiyun }
1784*4882a593Smuzhiyun
1785*4882a593Smuzhiyun /*****************************************************************************/
1786*4882a593Smuzhiyun
kbase_vinstr_init(struct kbase_device * kbdev)1787*4882a593Smuzhiyun struct kbase_vinstr_context *kbase_vinstr_init(struct kbase_device *kbdev)
1788*4882a593Smuzhiyun {
1789*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx;
1790*4882a593Smuzhiyun
1791*4882a593Smuzhiyun vinstr_ctx = kzalloc(sizeof(*vinstr_ctx), GFP_KERNEL);
1792*4882a593Smuzhiyun if (!vinstr_ctx)
1793*4882a593Smuzhiyun return NULL;
1794*4882a593Smuzhiyun
1795*4882a593Smuzhiyun INIT_LIST_HEAD(&vinstr_ctx->idle_clients);
1796*4882a593Smuzhiyun INIT_LIST_HEAD(&vinstr_ctx->waiting_clients);
1797*4882a593Smuzhiyun mutex_init(&vinstr_ctx->lock);
1798*4882a593Smuzhiyun spin_lock_init(&vinstr_ctx->state_lock);
1799*4882a593Smuzhiyun vinstr_ctx->kbdev = kbdev;
1800*4882a593Smuzhiyun vinstr_ctx->thread = NULL;
1801*4882a593Smuzhiyun vinstr_ctx->state = VINSTR_IDLE;
1802*4882a593Smuzhiyun vinstr_ctx->suspend_cnt = 0;
1803*4882a593Smuzhiyun INIT_WORK(&vinstr_ctx->suspend_work, kbasep_vinstr_suspend_worker);
1804*4882a593Smuzhiyun INIT_WORK(&vinstr_ctx->resume_work, kbasep_vinstr_resume_worker);
1805*4882a593Smuzhiyun init_waitqueue_head(&vinstr_ctx->suspend_waitq);
1806*4882a593Smuzhiyun
1807*4882a593Smuzhiyun atomic_set(&vinstr_ctx->request_pending, 0);
1808*4882a593Smuzhiyun init_waitqueue_head(&vinstr_ctx->waitq);
1809*4882a593Smuzhiyun
1810*4882a593Smuzhiyun return vinstr_ctx;
1811*4882a593Smuzhiyun }
1812*4882a593Smuzhiyun
kbase_vinstr_term(struct kbase_vinstr_context * vinstr_ctx)1813*4882a593Smuzhiyun void kbase_vinstr_term(struct kbase_vinstr_context *vinstr_ctx)
1814*4882a593Smuzhiyun {
1815*4882a593Smuzhiyun struct kbase_vinstr_client *cli;
1816*4882a593Smuzhiyun
1817*4882a593Smuzhiyun /* Stop service thread first. */
1818*4882a593Smuzhiyun if (vinstr_ctx->thread)
1819*4882a593Smuzhiyun kthread_stop(vinstr_ctx->thread);
1820*4882a593Smuzhiyun
1821*4882a593Smuzhiyun /* Wait for workers. */
1822*4882a593Smuzhiyun flush_work(&vinstr_ctx->suspend_work);
1823*4882a593Smuzhiyun flush_work(&vinstr_ctx->resume_work);
1824*4882a593Smuzhiyun
1825*4882a593Smuzhiyun while (1) {
1826*4882a593Smuzhiyun struct list_head *list = &vinstr_ctx->idle_clients;
1827*4882a593Smuzhiyun
1828*4882a593Smuzhiyun if (list_empty(list)) {
1829*4882a593Smuzhiyun list = &vinstr_ctx->waiting_clients;
1830*4882a593Smuzhiyun if (list_empty(list))
1831*4882a593Smuzhiyun break;
1832*4882a593Smuzhiyun }
1833*4882a593Smuzhiyun
1834*4882a593Smuzhiyun cli = list_first_entry(list, struct kbase_vinstr_client, list);
1835*4882a593Smuzhiyun list_del(&cli->list);
1836*4882a593Smuzhiyun kfree(cli->accum_buffer);
1837*4882a593Smuzhiyun kfree(cli);
1838*4882a593Smuzhiyun vinstr_ctx->nclients--;
1839*4882a593Smuzhiyun }
1840*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(!vinstr_ctx->nclients);
1841*4882a593Smuzhiyun if (vinstr_ctx->kctx)
1842*4882a593Smuzhiyun kbasep_vinstr_destroy_kctx(vinstr_ctx);
1843*4882a593Smuzhiyun kfree(vinstr_ctx);
1844*4882a593Smuzhiyun }
1845*4882a593Smuzhiyun
kbase_vinstr_hwcnt_reader_setup(struct kbase_vinstr_context * vinstr_ctx,struct kbase_uk_hwcnt_reader_setup * setup)1846*4882a593Smuzhiyun int kbase_vinstr_hwcnt_reader_setup(struct kbase_vinstr_context *vinstr_ctx,
1847*4882a593Smuzhiyun struct kbase_uk_hwcnt_reader_setup *setup)
1848*4882a593Smuzhiyun {
1849*4882a593Smuzhiyun struct kbase_vinstr_client *cli;
1850*4882a593Smuzhiyun u32 bitmap[4];
1851*4882a593Smuzhiyun
1852*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
1853*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(setup);
1854*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(setup->buffer_count);
1855*4882a593Smuzhiyun
1856*4882a593Smuzhiyun bitmap[SHADER_HWCNT_BM] = setup->shader_bm;
1857*4882a593Smuzhiyun bitmap[TILER_HWCNT_BM] = setup->tiler_bm;
1858*4882a593Smuzhiyun bitmap[MMU_L2_HWCNT_BM] = setup->mmu_l2_bm;
1859*4882a593Smuzhiyun bitmap[JM_HWCNT_BM] = setup->jm_bm;
1860*4882a593Smuzhiyun
1861*4882a593Smuzhiyun cli = kbasep_vinstr_attach_client(
1862*4882a593Smuzhiyun vinstr_ctx,
1863*4882a593Smuzhiyun setup->buffer_count,
1864*4882a593Smuzhiyun bitmap,
1865*4882a593Smuzhiyun &setup->fd,
1866*4882a593Smuzhiyun NULL);
1867*4882a593Smuzhiyun
1868*4882a593Smuzhiyun if (!cli)
1869*4882a593Smuzhiyun return -ENOMEM;
1870*4882a593Smuzhiyun
1871*4882a593Smuzhiyun return 0;
1872*4882a593Smuzhiyun }
1873*4882a593Smuzhiyun
kbase_vinstr_legacy_hwc_setup(struct kbase_vinstr_context * vinstr_ctx,struct kbase_vinstr_client ** cli,struct kbase_uk_hwcnt_setup * setup)1874*4882a593Smuzhiyun int kbase_vinstr_legacy_hwc_setup(
1875*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx,
1876*4882a593Smuzhiyun struct kbase_vinstr_client **cli,
1877*4882a593Smuzhiyun struct kbase_uk_hwcnt_setup *setup)
1878*4882a593Smuzhiyun {
1879*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
1880*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(setup);
1881*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(cli);
1882*4882a593Smuzhiyun
1883*4882a593Smuzhiyun if (setup->dump_buffer) {
1884*4882a593Smuzhiyun u32 bitmap[4];
1885*4882a593Smuzhiyun
1886*4882a593Smuzhiyun bitmap[SHADER_HWCNT_BM] = setup->shader_bm;
1887*4882a593Smuzhiyun bitmap[TILER_HWCNT_BM] = setup->tiler_bm;
1888*4882a593Smuzhiyun bitmap[MMU_L2_HWCNT_BM] = setup->mmu_l2_bm;
1889*4882a593Smuzhiyun bitmap[JM_HWCNT_BM] = setup->jm_bm;
1890*4882a593Smuzhiyun
1891*4882a593Smuzhiyun if (*cli)
1892*4882a593Smuzhiyun return -EBUSY;
1893*4882a593Smuzhiyun
1894*4882a593Smuzhiyun *cli = kbasep_vinstr_attach_client(
1895*4882a593Smuzhiyun vinstr_ctx,
1896*4882a593Smuzhiyun 0,
1897*4882a593Smuzhiyun bitmap,
1898*4882a593Smuzhiyun (void *)(long)setup->dump_buffer,
1899*4882a593Smuzhiyun NULL);
1900*4882a593Smuzhiyun
1901*4882a593Smuzhiyun if (!(*cli))
1902*4882a593Smuzhiyun return -ENOMEM;
1903*4882a593Smuzhiyun } else {
1904*4882a593Smuzhiyun if (!*cli)
1905*4882a593Smuzhiyun return -EINVAL;
1906*4882a593Smuzhiyun
1907*4882a593Smuzhiyun kbase_vinstr_detach_client(*cli);
1908*4882a593Smuzhiyun *cli = NULL;
1909*4882a593Smuzhiyun }
1910*4882a593Smuzhiyun
1911*4882a593Smuzhiyun return 0;
1912*4882a593Smuzhiyun }
1913*4882a593Smuzhiyun
kbase_vinstr_hwcnt_kernel_setup(struct kbase_vinstr_context * vinstr_ctx,struct kbase_uk_hwcnt_reader_setup * setup,void * kernel_buffer)1914*4882a593Smuzhiyun struct kbase_vinstr_client *kbase_vinstr_hwcnt_kernel_setup(
1915*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx,
1916*4882a593Smuzhiyun struct kbase_uk_hwcnt_reader_setup *setup,
1917*4882a593Smuzhiyun void *kernel_buffer)
1918*4882a593Smuzhiyun {
1919*4882a593Smuzhiyun u32 bitmap[4];
1920*4882a593Smuzhiyun
1921*4882a593Smuzhiyun if (!vinstr_ctx || !setup || !kernel_buffer)
1922*4882a593Smuzhiyun return NULL;
1923*4882a593Smuzhiyun
1924*4882a593Smuzhiyun bitmap[SHADER_HWCNT_BM] = setup->shader_bm;
1925*4882a593Smuzhiyun bitmap[TILER_HWCNT_BM] = setup->tiler_bm;
1926*4882a593Smuzhiyun bitmap[MMU_L2_HWCNT_BM] = setup->mmu_l2_bm;
1927*4882a593Smuzhiyun bitmap[JM_HWCNT_BM] = setup->jm_bm;
1928*4882a593Smuzhiyun
1929*4882a593Smuzhiyun return kbasep_vinstr_attach_client(
1930*4882a593Smuzhiyun vinstr_ctx,
1931*4882a593Smuzhiyun 0,
1932*4882a593Smuzhiyun bitmap,
1933*4882a593Smuzhiyun NULL,
1934*4882a593Smuzhiyun kernel_buffer);
1935*4882a593Smuzhiyun }
1936*4882a593Smuzhiyun KBASE_EXPORT_TEST_API(kbase_vinstr_hwcnt_kernel_setup);
1937*4882a593Smuzhiyun
kbase_vinstr_hwc_dump(struct kbase_vinstr_client * cli,enum base_hwcnt_reader_event event_id)1938*4882a593Smuzhiyun int kbase_vinstr_hwc_dump(struct kbase_vinstr_client *cli,
1939*4882a593Smuzhiyun enum base_hwcnt_reader_event event_id)
1940*4882a593Smuzhiyun {
1941*4882a593Smuzhiyun int rcode = 0;
1942*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx;
1943*4882a593Smuzhiyun u64 timestamp;
1944*4882a593Smuzhiyun u32 event_mask;
1945*4882a593Smuzhiyun
1946*4882a593Smuzhiyun if (!cli)
1947*4882a593Smuzhiyun return -EINVAL;
1948*4882a593Smuzhiyun
1949*4882a593Smuzhiyun vinstr_ctx = cli->vinstr_ctx;
1950*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
1951*4882a593Smuzhiyun
1952*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(event_id < BASE_HWCNT_READER_EVENT_COUNT);
1953*4882a593Smuzhiyun event_mask = 1 << event_id;
1954*4882a593Smuzhiyun
1955*4882a593Smuzhiyun mutex_lock(&vinstr_ctx->lock);
1956*4882a593Smuzhiyun
1957*4882a593Smuzhiyun if (event_mask & cli->event_mask) {
1958*4882a593Smuzhiyun rcode = kbasep_vinstr_collect_and_accumulate(
1959*4882a593Smuzhiyun vinstr_ctx,
1960*4882a593Smuzhiyun ×tamp);
1961*4882a593Smuzhiyun if (rcode)
1962*4882a593Smuzhiyun goto exit;
1963*4882a593Smuzhiyun
1964*4882a593Smuzhiyun rcode = kbasep_vinstr_update_client(cli, timestamp, event_id);
1965*4882a593Smuzhiyun if (rcode)
1966*4882a593Smuzhiyun goto exit;
1967*4882a593Smuzhiyun
1968*4882a593Smuzhiyun kbasep_vinstr_reprogram(vinstr_ctx);
1969*4882a593Smuzhiyun }
1970*4882a593Smuzhiyun
1971*4882a593Smuzhiyun exit:
1972*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
1973*4882a593Smuzhiyun
1974*4882a593Smuzhiyun return rcode;
1975*4882a593Smuzhiyun }
1976*4882a593Smuzhiyun KBASE_EXPORT_TEST_API(kbase_vinstr_hwc_dump);
1977*4882a593Smuzhiyun
kbase_vinstr_hwc_clear(struct kbase_vinstr_client * cli)1978*4882a593Smuzhiyun int kbase_vinstr_hwc_clear(struct kbase_vinstr_client *cli)
1979*4882a593Smuzhiyun {
1980*4882a593Smuzhiyun struct kbase_vinstr_context *vinstr_ctx;
1981*4882a593Smuzhiyun int rcode;
1982*4882a593Smuzhiyun u64 unused;
1983*4882a593Smuzhiyun
1984*4882a593Smuzhiyun if (!cli)
1985*4882a593Smuzhiyun return -EINVAL;
1986*4882a593Smuzhiyun
1987*4882a593Smuzhiyun vinstr_ctx = cli->vinstr_ctx;
1988*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
1989*4882a593Smuzhiyun
1990*4882a593Smuzhiyun mutex_lock(&vinstr_ctx->lock);
1991*4882a593Smuzhiyun
1992*4882a593Smuzhiyun rcode = kbasep_vinstr_collect_and_accumulate(vinstr_ctx, &unused);
1993*4882a593Smuzhiyun if (rcode)
1994*4882a593Smuzhiyun goto exit;
1995*4882a593Smuzhiyun rcode = kbase_instr_hwcnt_clear(vinstr_ctx->kctx);
1996*4882a593Smuzhiyun if (rcode)
1997*4882a593Smuzhiyun goto exit;
1998*4882a593Smuzhiyun memset(cli->accum_buffer, 0, cli->dump_size);
1999*4882a593Smuzhiyun
2000*4882a593Smuzhiyun kbasep_vinstr_reprogram(vinstr_ctx);
2001*4882a593Smuzhiyun
2002*4882a593Smuzhiyun exit:
2003*4882a593Smuzhiyun mutex_unlock(&vinstr_ctx->lock);
2004*4882a593Smuzhiyun
2005*4882a593Smuzhiyun return rcode;
2006*4882a593Smuzhiyun }
2007*4882a593Smuzhiyun
kbase_vinstr_try_suspend(struct kbase_vinstr_context * vinstr_ctx)2008*4882a593Smuzhiyun int kbase_vinstr_try_suspend(struct kbase_vinstr_context *vinstr_ctx)
2009*4882a593Smuzhiyun {
2010*4882a593Smuzhiyun unsigned long flags;
2011*4882a593Smuzhiyun int ret = -EAGAIN;
2012*4882a593Smuzhiyun
2013*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
2014*4882a593Smuzhiyun
2015*4882a593Smuzhiyun spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
2016*4882a593Smuzhiyun switch (vinstr_ctx->state) {
2017*4882a593Smuzhiyun case VINSTR_SUSPENDED:
2018*4882a593Smuzhiyun vinstr_ctx->suspend_cnt++;
2019*4882a593Smuzhiyun /* overflow shall not happen */
2020*4882a593Smuzhiyun BUG_ON(0 == vinstr_ctx->suspend_cnt);
2021*4882a593Smuzhiyun ret = 0;
2022*4882a593Smuzhiyun break;
2023*4882a593Smuzhiyun
2024*4882a593Smuzhiyun case VINSTR_IDLE:
2025*4882a593Smuzhiyun vinstr_ctx->state = VINSTR_SUSPENDING;
2026*4882a593Smuzhiyun schedule_work(&vinstr_ctx->suspend_work);
2027*4882a593Smuzhiyun break;
2028*4882a593Smuzhiyun
2029*4882a593Smuzhiyun case VINSTR_DUMPING:
2030*4882a593Smuzhiyun vinstr_ctx->state = VINSTR_SUSPENDING;
2031*4882a593Smuzhiyun break;
2032*4882a593Smuzhiyun
2033*4882a593Smuzhiyun case VINSTR_SUSPENDING:
2034*4882a593Smuzhiyun /* fall through */
2035*4882a593Smuzhiyun case VINSTR_RESUMING:
2036*4882a593Smuzhiyun break;
2037*4882a593Smuzhiyun
2038*4882a593Smuzhiyun default:
2039*4882a593Smuzhiyun BUG();
2040*4882a593Smuzhiyun break;
2041*4882a593Smuzhiyun }
2042*4882a593Smuzhiyun spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
2043*4882a593Smuzhiyun
2044*4882a593Smuzhiyun return ret;
2045*4882a593Smuzhiyun }
2046*4882a593Smuzhiyun
kbase_vinstr_suspend(struct kbase_vinstr_context * vinstr_ctx)2047*4882a593Smuzhiyun void kbase_vinstr_suspend(struct kbase_vinstr_context *vinstr_ctx)
2048*4882a593Smuzhiyun {
2049*4882a593Smuzhiyun wait_event(vinstr_ctx->suspend_waitq,
2050*4882a593Smuzhiyun (0 == kbase_vinstr_try_suspend(vinstr_ctx)));
2051*4882a593Smuzhiyun }
2052*4882a593Smuzhiyun
kbase_vinstr_resume(struct kbase_vinstr_context * vinstr_ctx)2053*4882a593Smuzhiyun void kbase_vinstr_resume(struct kbase_vinstr_context *vinstr_ctx)
2054*4882a593Smuzhiyun {
2055*4882a593Smuzhiyun unsigned long flags;
2056*4882a593Smuzhiyun
2057*4882a593Smuzhiyun KBASE_DEBUG_ASSERT(vinstr_ctx);
2058*4882a593Smuzhiyun
2059*4882a593Smuzhiyun spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
2060*4882a593Smuzhiyun BUG_ON(VINSTR_SUSPENDING == vinstr_ctx->state);
2061*4882a593Smuzhiyun if (VINSTR_SUSPENDED == vinstr_ctx->state) {
2062*4882a593Smuzhiyun BUG_ON(0 == vinstr_ctx->suspend_cnt);
2063*4882a593Smuzhiyun vinstr_ctx->suspend_cnt--;
2064*4882a593Smuzhiyun if (0 == vinstr_ctx->suspend_cnt) {
2065*4882a593Smuzhiyun vinstr_ctx->state = VINSTR_RESUMING;
2066*4882a593Smuzhiyun schedule_work(&vinstr_ctx->resume_work);
2067*4882a593Smuzhiyun }
2068*4882a593Smuzhiyun }
2069*4882a593Smuzhiyun spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
2070*4882a593Smuzhiyun }
2071