blob: b83a40d181b5829412cdbd8a6e1f98a6a72dcfa9 [file] [log] [blame]
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001/*
2 **************************************************************************
Guojun Jina09b8b02018-01-25 16:34:43 -08003 * Copyright (c) 2014,2016,2018 The Linux Foundation. All rights reserved.
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08004 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all copies.
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
13 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 **************************************************************************
15 */
16
17/*
18 * qsdk/qca/src/qca-nss-drv/profiler/profile.c
19 *
20 * Implementation for NetAP Profiler
21 */
22
23#include <linux/platform_device.h>
Murat Sezgin3441e772015-10-26 11:55:57 -070024#include <linux/of.h>
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -080025#include <linux/export.h>
26#include <linux/module.h>
27#include <linux/seq_file.h>
28#include <linux/proc_fs.h>
29#include <linux/mm.h>
30#include <linux/mmzone.h>
31#include <linux/fs.h>
32#include <linux/page-flags.h>
33#include <linux/sched.h>
34#include <asm/uaccess.h>
35#include <asm/page.h>
36#include <asm/thread_info.h>
Guojun Jin3deae8c2016-08-23 15:51:21 -070037#include <linux/ctype.h>
Sundarajan Srinivasand09d7dd2014-12-10 16:24:21 -080038#include <nss_api_if.h>
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -080039
40#include "profilenode.h"
41#include "profpkt.h"
42
43/*
44 * This is the driver for the NetAP Core profiler. The system interface to the driver is
45 * profile_register_performance_counter(), defined in <asm/profile.>
46 * a set of proc files (proc/profile/<*>), used by the profiler daemon
47 *
48 * communication between the profiler components is described in a set of header files.
49 * There are multiple versions of these files that must be kept synchronized:
50 * in nss/source/pkg/profile
51 * in tools/profiler
52 * in qsdk/qca/src/qca-nss-drv/profiler
53 *
54 * profilesample.h specifies the sample format used by pkg/profile, profile driver, and ip3kprof (two versions)
55 * profilenode.h specifies the driver node communication between NetAP and the profile driver. (two versions)
56 * profpkt.h specifies the network packet format between the profile driver, profile daemon, and ip3kprof (two versions)
57 *
58 *
59 * NSS profile sampler:
60 * pkg/profile/src/profile.c
61 * pkg/profile/include/profilenode.h
62 * pkg/profile/include/profilesample.h
63 *
64 * profile driver: this code
65 * qsdk/qca/src/qca-nss-drv/profiler
66 *
67 * profilerd: the user daemon that sends data to the tool
68 * qsdk/qca/feeds/qca/utils/profilerd
69 *
70 * ubicom32-prof: the Windows tool
71 * tools/profiler/src/(many files)
72 *
73 */
74
75#ifdef PROFILE_DEBUG
76#define profileDebug(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
77#define profileInfo(s, ...) pr_info("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
78#define profileWarn(s, ...) pr_warn("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
79#else
80#define profileDebug(s, ...)
81#define profileInfo(s, ...)
82#define profileWarn(s, ...)
83#endif
84
85static void profiler_handle_reply(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm);
86
87/*
88 * LINUX and Ultra counters must all fit in one packet
89 */
90#define PROFILE_LINUX_MAX_COUNTERS 40
Guojun Jin3deae8c2016-08-23 15:51:21 -070091#define PROFILE_STS_EVENT_COUNTERS 8
92#define PROFILE_STS_EVENT_THREAD_BITS 5
93
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -080094static int profile_num_counters = 0;
95static volatile unsigned int *profile_counter[PROFILE_LINUX_MAX_COUNTERS];
96static char profile_name[PROFILE_LINUX_MAX_COUNTERS][PROFILE_COUNTER_NAME_LENGTH];
97
98/*
99 * internal function to check if @name has been registered before
100 * return the found index, or -1 otherwise
101 */
102static int __profile_find_entry(char *name)
103{
104 int i;
105
106 for (i = 0; i < profile_num_counters; i++) {
107 if (!strncasecmp(name, profile_name[i], PROFILE_COUNTER_NAME_LENGTH)) {
108 return i;
109 }
110 }
111 return -1;
112}
113
114/*
115 * profile_register_performance_counter - register @counter into profile tracking list by key @name
116 * @counter: pointer of the counter variable
117 * @name: identifier of this counter
118 *
119 * Returns zero if total entries exceeding PROFILE_LINUX_MAX_COUNTERS
120 * non-zero otherwise.
121 *
122 * Each @name gives unique entry for @counter, by allocating a new array slot or just use existing one.
123 * No need of de-registration API, since a loadable module's new insmod, will replace the
124 * @counter's * new address at the same profile_counter[] slot.
125 */
126int profile_register_performance_counter(volatile unsigned int *counter, char *name)
127{
128 int i;
129
130 if (profile_num_counters >= PROFILE_LINUX_MAX_COUNTERS) {
131 return 0;
132 }
133
134 i = __profile_find_entry(name);
135 if (i < 0) {
136 i = profile_num_counters++;
137 }
138
139 profile_counter[i] = counter;
Guojun Jind3328392016-01-22 14:14:17 -0800140 strlcpy(profile_name[i], name, PROFILE_COUNTER_NAME_LENGTH);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800141 profile_name[i][PROFILE_COUNTER_NAME_LENGTH - 1] = 0;
142
143 return 1;
144}
145
146/*
Guojun Jina09b8b02018-01-25 16:34:43 -0800147 * profile_make_data_packet
148 * Make a packet full of sample data
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800149 */
150static int profile_make_data_packet(char *buf, int blen, struct profile_io *pn)
151{
152 int sp_samples = 0; /* separated samples if any */
153 int ns; /* number of samples requested */
154 struct profile_header ph;
155 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
156
157 if (blen < sizeof(ph) + sizeof(struct profile_sample)) {
158 return -EINVAL;
159 }
160
161 profileDebug("%p stat %x cnt %d %p\n", pn->pnc.pn2h, pn->pnc.pn2h->mh.md_type, psc_hd->count, pn->ccl);
162
163 if (pn->pnc.pn2h->mh.md_type == PINGPONG_EMPTY || psc_hd->count < 1) {
164 struct profile_n2h_sample_buf *nsb;
165 ns = (pn->ccl_read + 1) & (CCL_SIZE-1);
166 nsb = pn->ccl + ns;
167 if (ns == pn->ccl_write || nsb->mh.md_type != PINGPONG_FULL) {
168 profileInfo("%s: waiting more data %x %p : ns %d rd %d wr %d\n", __func__, nsb->mh.md_type, nsb, ns, pn->ccl_read, pn->ccl_write);
169 return -EAGAIN;
170 }
171 pn->ccl_read = ns;
172 profileInfo("sp %p => %p rd %d %p\n", pn->pnc.samples, nsb->samples, ns, nsb);
173 psc_hd = &nsb->psc_header;
174 pn->pnc.pn2h = nsb;
175 pn->pnc.samples = nsb->samples;
176 pn->pnc.cur = 0;
177 }
178 pn->pnc.pn2h->mh.md_type = PINGPONG_INUSE;
179
180 /*
181 * fill in the packet header
182 */
183 memset(&ph, 0, sizeof(ph));
184 ph.pph.magic = htons(PROF_MAGIC + PROFILE_VERSION);
185 ph.pph.header_size = sizeof(ph);
186 ph.pph.profile_instructions = 0;
187 ph.pph.clock_freq = pn->pnc.un.cpu_freq;
188 ph.pph.ddr_freq = pn->pnc.un.ddr_freq;
189 ph.pph.cpu_id = pn->pnc.un.cpu_id;
190 ph.pph.seq_num = htonl(pn->profile_sequence_num);
Guojun Jin3deae8c2016-08-23 15:51:21 -0700191 ph.pph.sample_stack_words = PROFILE_STACK_WORDS;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800192
193 ns = (blen - sizeof(ph)) / sizeof(struct profile_sample);
Guojun Jina09b8b02018-01-25 16:34:43 -0800194 profileInfo("%X: blen %d ns = %d psc_hd count %d ssets %d phs %lu pss %lu\n",
195 pn->profile_sequence_num, blen, ns, psc_hd->count,
196 psc_hd->exh.sample_sets, sizeof(ph), sizeof(struct profile_sample));
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800197 if (ns > psc_hd->count)
198 ns = psc_hd->count;
199 if (ns == 0) {
200 printk("NS should not be 0: rlen %d hd cnt %d\n", blen, psc_hd->count);
201 return 0;
202 }
203
204 /*
205 * if buf cannot hold all samples, then samples must be separated by set.
206 */
207 if (ns < psc_hd->count) {
208 ph.exh.sets_map = psc_hd->exh.sets_map; /* save for separating sets */
209 do {
210 sp_samples += psc_hd->exh.sets_map & 0x0F;
211 psc_hd->exh.sets_map >>= 4; /* remove the last set */
212 psc_hd->exh.sample_sets--;
213 ph.exh.sample_sets++; /* save for restore later */
214 } while ((psc_hd->count - sp_samples) > ns);
215 ns = psc_hd->count - sp_samples;
216 }
217 ph.pph.sample_count = ns;
218 if (copy_to_user(buf, &ph.pph, sizeof(ph.pph)) != 0) {
219 return -EFAULT;
220 }
221 buf += sizeof(ph.pph);
222
223 /*
Guojun Jina09b8b02018-01-25 16:34:43 -0800224 * ph.exh is unused dummy; and psc_hd->exh is used directly to avoid double mem copy
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800225 */
226 if (copy_to_user(buf, &psc_hd->exh, sizeof(psc_hd->exh)) != 0) {
227 return -EFAULT;
228 }
229 buf += sizeof(psc_hd->exh);
230
231 blen = ns * sizeof(struct profile_sample);
232 profileDebug("-profile_make_data_packet %p slen %d cur %d dcped %d + %d\n", pn->pnc.samples, blen, pn->pnc.cur, sizeof(ph.pph), sizeof(psc_hd->exh));
233 if (copy_to_user(buf, &pn->pnc.samples[pn->pnc.cur], blen) != 0) {
234 return -EFAULT;
235 }
236 pn->pnc.cur += ns;
237 psc_hd->count -= ns;
238 if (psc_hd->count < 1)
239 pn->pnc.pn2h->mh.md_type = PINGPONG_EMPTY;
240
241 /*
242 * restore left over sample counts; 0s for no one
243 */
244 if (sp_samples) {
245 profileDebug("%d sps %d %d: sets %d : %d map %x <> %x\n", psc_hd->count, ns, sp_samples, psc_hd->exh.sample_sets, ph.exh.sample_sets, psc_hd->exh.sets_map, ph.exh.sets_map);
246 psc_hd->exh.sample_sets = ph.exh.sample_sets;
247 psc_hd->exh.sets_map = ph.exh.sets_map;
248 }
249
250 pn->profile_sequence_num++;
251 blen += sizeof(ph);
252 profileDebug("+profile_make_data_packet %d phd len %d nsp %p rd %d cnt %d\n", blen, sizeof(ph), pn->pnc.pn2h, pn->ccl_read, psc_hd->count);
253 return blen;
254}
255
256/*
257 * This is no longer needed due to NetAP and Linux use different CPUs, and profile is NetAP only.
258 * All related code will be removed after corresponging code in visual tool is corrected; otherwise
259 * visual tool will mis-behave
260 */
261struct profile_counter profile_builtin_stats[] =
262{
263 {
264 "Free memory(KB)", 0
265 },
266 {
267 "Max free Block(KB)", 0
268 }
269};
270
271/*
Guojun Jina09b8b02018-01-25 16:34:43 -0800272 * profile_make_stats_packet
273 * make a packet full of performance counters (software)
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800274 */
275static int profile_make_stats_packet(char *buf, int bytes, struct profile_io *pn)
276{
277 static char prof_pkt[PROFILE_MAX_PACKET_SIZE];
278
279 char *ptr;
280 int n;
281 struct profile_counter *counter_ptr;
282 struct profile_header_counters *hdr = (struct profile_header_counters *)prof_pkt;
283 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
284
285 if (bytes > PROFILE_MAX_PACKET_SIZE) {
286 bytes = PROFILE_MAX_PACKET_SIZE;
287 }
288 n = sizeof(profile_builtin_stats) + (pn->pnc.un.num_counters + profile_num_counters) * sizeof(*counter_ptr);
289
290 if ((bytes - sizeof(hdr)) < n) {
291 profileWarn("room too small %d for cnts %d\n", bytes, n);
292 return 0;
293 }
294
295 hdr->magic = htons(PROF_MAGIC_COUNTERS);
296 hdr->ultra_count = htons(pn->pnc.un.num_counters);
297 hdr->linux_count = htonl(profile_num_counters + sizeof(profile_builtin_stats) / sizeof(*counter_ptr));
298 hdr->ultra_sample_time = psc_hd->exh.clocks;
299 hdr->linux_sample_time = psc_hd->exh.clocks; /* QSDK has no time func */
300
301 n = pn->pnc.un.num_counters; /* copy NSS counters */
302 n *= sizeof(pn->pnc.un.counters[0]);
303 ptr = (char*) (hdr + 1);
304 memcpy(ptr, (void *)(pn->pnc.un.counters), n);
305 ptr += n;
306
307 counter_ptr = (struct profile_counter *)ptr;
308 for (n = 0; n < profile_num_counters; ++n) {
309 counter_ptr->value = htonl(*profile_counter[n]);
Guojun Jind3328392016-01-22 14:14:17 -0800310 strlcpy(counter_ptr->name, profile_name[n],
311 PROFILE_COUNTER_NAME_LENGTH);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800312 counter_ptr++;
313 }
314 ptr = (char*)counter_ptr;
315
316 /*
317 * built in statistics
318 profile_get_memory_stats(&total_free, &max_free);
319 */
320 profile_builtin_stats[0].value = 0;
321 profile_builtin_stats[1].value = 0;
322 memcpy(ptr, (void *)profile_builtin_stats, sizeof(profile_builtin_stats));
323 ptr += sizeof(profile_builtin_stats);
324
325 n = ptr - prof_pkt;
326 if (copy_to_user(buf, prof_pkt, n) != 0) {
327 return -EFAULT;
328 }
329 return n;
330}
331
332/*
333 * space for all memory blocks so we can hold locks for short time when walking tables
334 */
335static struct profile_io *node[NSS_MAX_CORES];
336
Guojun Jina09b8b02018-01-25 16:34:43 -0800337/*
338 * profile_open
339 * open function of system call
340 */
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800341static int profile_open(struct inode *inode, struct file *filp)
342{
343 int n;
344 struct profile_io *pn;
345
346 if (filp->private_data)
Shashank Balashankar0b352e62016-03-29 15:48:36 -0700347 printk(KERN_WARNING "%s: %p\n", filp->f_path.dentry->d_iname, filp->private_data);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800348
Shashank Balashankar0b352e62016-03-29 15:48:36 -0700349 n = filp->f_path.dentry->d_iname[strlen(filp->f_path.dentry->d_iname) - 1] - '0';
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800350 if (n < 0 || n >= NSS_MAX_CORES)
351 n = 0;
352 pn = node[n];
353 if (!pn) {
354 return -ENOENT;
355 }
356
Guojun Jina09b8b02018-01-25 16:34:43 -0800357 profileInfo("_open: mode %x flag %x\n", filp->f_mode, filp->f_flags);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800358 if (!pn->pnc.enabled && nss_get_state(pn->ctx) == NSS_STATE_INITIALIZED) {
Guojun Jin3deae8c2016-08-23 15:51:21 -0700359 /*
360 * sw_ksp_ptr is used as event flag. NULL means normal I/O
361 */
362 pn->sw_ksp_ptr = NULL;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800363 pn->pnc.enabled = 1;
364 pn->profile_first_packet = 1;
Guojun Jina09b8b02018-01-25 16:34:43 -0800365
366 /*
367 * If profiler is opened in read only mode, it is done by START_MSG
368 * via debug interface (IF), which reads NSS-FW all registered NSS
369 * variables.
370 * Do not start engine (no sampling required) for debug IF.
371 */
372 if (FMODE_READ & filp->f_mode) {
373 nss_tx_status_t ret;
374
375 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_START_MSG;
376 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un,
Guojun Jin3deae8c2016-08-23 15:51:21 -0700377 sizeof(pn->pnc.un), profiler_handle_reply, pn);
Guojun Jina09b8b02018-01-25 16:34:43 -0800378 profileInfo("%s: %d -- %p: ccl %p sp %p\n", __func__, ret,
379 pn, pn->ccl, pn->pnc.samples);
380 }
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800381 filp->private_data = pn;
382 return 0;
383 }
384
Guojun Jin3deae8c2016-08-23 15:51:21 -0700385 profileWarn("profile ena %d nss stat %x\n", pn->pnc.enabled,
386 nss_get_state(pn->ctx));
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800387 return -EBUSY;
388}
389
390/*
Guojun Jina09b8b02018-01-25 16:34:43 -0800391 * profile_read
392 * read syscall
393 *
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800394 * return a udp packet ready to send to the profiler tool
395 * when there are no packets left to make, return 0
396 */
Guojun Jin76cf1392017-05-02 12:02:31 -0700397static ssize_t profile_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800398{
399 int result = 0;
400 int slen = 0;
401 struct profile_io *pn = (struct profile_io *)filp->private_data;
402 if (!pn) {
403 return -ENOENT;
404 }
405
406 if (!pn->pnc.enabled) {
407 return -EPERM;
408 }
Guojun Jin3deae8c2016-08-23 15:51:21 -0700409 if (pn->sw_ksp_ptr) {
410 struct debug_box *db = (struct debug_box *) pn->sw_ksp_ptr;
411 slen = (PROFILE_STS_EVENT_COUNTERS + 1) * sizeof(db->data[0]);
412 if (copy_to_user(buf, db->data, slen))
413 return -EFAULT;
414 return slen;
415 }
416
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800417 if (!pn->pnc.samples) {
418 return -ENOMEM;
419 }
420
421 if (pn->profile_first_packet) {
422 result = profile_make_stats_packet(buf, count, pn);
423 pn->profile_first_packet = 0;
Guojun Jin76cf1392017-05-02 12:02:31 -0700424 profileInfo("%d profile_make_stats_packet %zd\n", result, count);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800425
426#ifdef PROFILE_SEP_STAT
427 /*
428 * currectly, stat and sample data are combined in one pkt for efficient;
429 * but this is harder to debug and required remote tool to handle
430 * packet in all-in-one method instead of individual handler.
431 */
432 return result;
433#endif
434 }
435
436 if (result > 0) {
437 buf += result;
438 count -= result;
439 slen = result;
440 }
441 result = profile_make_data_packet(buf, count, pn);
442 if (result == 0) {
443 pn->profile_first_packet = 1;
444 }
Guojun Jin76cf1392017-05-02 12:02:31 -0700445 profileInfo("%d: profile_make_data_packet %zd %d\n", result, count, slen);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800446
447 profileInfo("%d: read\n", pn->pnc.enabled);
448 if (pn->pnc.enabled < 0) {
449 nss_tx_status_t ret;
450 pn->pnc.enabled = 1;
451 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_START_MSG;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700452 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un, sizeof(pn->pnc.un),
453 profiler_handle_reply, pn);
454 profileWarn("%s: restart %d -- %p: ccl %p sp %p\n", __func__,
455 ret, pn, pn->ccl, pn->pnc.samples);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800456 }
457
458 return result + slen;
459}
460
461/*
Guojun Jina09b8b02018-01-25 16:34:43 -0800462 * profile_release
463 * the close syscall paired with profiler_open
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800464 */
465static int profile_release(struct inode *inode, struct file *filp)
466{
467 struct profile_io *pn = (struct profile_io *)filp->private_data;
468 if (!pn) {
469 return -ENOENT;
470 }
471
472 if (pn->pnc.enabled) {
473 nss_tx_status_t ret;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700474 pn->sw_ksp_ptr = NULL;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800475 pn->pnc.enabled = 0;
476 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_STOP_MSG;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700477 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un,
478 sizeof(pn->pnc.un), profiler_handle_reply, pn);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800479 profileInfo("%s: %p %d\n", __func__, pn, ret);
480 return 0;
481 }
482 profileWarn("%s: attempt closing non-open dev %p\n", __func__, pn);
483 pn->profile_first_packet = 1;
484 return -EBADF;
485}
486
Guojun Jina09b8b02018-01-25 16:34:43 -0800487#ifndef __aarch64__
488/*
489 * counter_rate_by_uint32
490 * helper function for handling 64-bit calculation in 32-bit mode
491 *
492 * 32-bit kernel does not have 64-bit div function;
493 * to avoid overflow and underflow, use if branch
494 * to overcome this problem: slower bur more accurate.
495 */
496static void counter_rate_by_uint32(struct profile_common *pnc)
497{
498 static uint32_t prev_cnts[32];
499 static uint32_t last_uclk;
Guojun Jinb1642792018-03-20 16:11:26 -0700500 uint32_t mclk, uclk, ubi32_freq;
Guojun Jina09b8b02018-01-25 16:34:43 -0800501 int n = pnc->un.num_counters;
502
503 ubi32_freq = htonl(pnc->un.cpu_freq) / 1000000;
504 uclk = pnc->un.rate - last_uclk;
505 last_uclk = pnc->un.rate;
506 printk("%d nss counters: clk dif %u freq %u\n", n, uclk, ubi32_freq);
507
508 /*
509 * exactly 4G? make it maximum
510 */
511 if (!uclk)
512 uclk--;
513 while (n--) {
514 uint32_t v_dif;
515 uint32_t v = ntohl(pnc->un.counters[n].value);
516 uint32_t pv = prev_cnts[n];
517
518 prev_cnts[n] = v;
519 v_dif = v - pv;
520
521 /*
522 * threshold = MAX_UINT32 / MAX_Ubi32CPU_CLK (MHz)
523 * if counter diff is less then this threshold,
524 * 32-bit calculation can be directly applied w/o o/u flow;
525 * otherwise, tick diff (uclk) adjust needs to be done before
526 * calculating the rate to avoid over/under flow.
527 */
528 if (v_dif < (UINT_MAX / ubi32_freq)) {
529 v_dif = (v_dif * ubi32_freq) / (uclk / 1000000);
530 } else {
531 /*
532 * assume fast polling is 200ms, @ 500MHz, the minimum
533 * uclk value is 0.5M * 200 = 10M, so reduce by 1M
534 * it will still have value in 10, not zero (0).
535 * in 2.3GHz and 1 sec interval, the residual is 2300.
536 * The maximum polling interval is 2 sec for 2.3GHz,
537 * and 3 sec for 1.7GHz.
538 */
539 if (uclk > 1000000) {
Guojun Jinb1642792018-03-20 16:11:26 -0700540 mclk = uclk / 1000000;
541 v_dif = (v_dif / mclk) * ubi32_freq;
Guojun Jina09b8b02018-01-25 16:34:43 -0800542 } else {
Guojun Jinb1642792018-03-20 16:11:26 -0700543 mclk = uclk / 1000;
544 v_dif = (v_dif / mclk) * ubi32_freq * 1000;
Guojun Jina09b8b02018-01-25 16:34:43 -0800545 }
546 }
547 printk("%-32s 0x%08X %10u : %u/s\n",
548 pnc->un.counters[n].name, v, v, v_dif);
549 }
550}
551#endif
552
553/*
554 * profiler_handle_counter_event_reply()
555 * get reply from firmware for current FW stat event counter configurations
556 *
557 * Based on firmware CPU clock (cpu_freq), calculate the counter change rate in
558 * second and print both counter value and its rate.
559 */
560static void profiler_handle_counter_event_reply(struct nss_ctx_instance *nss_ctx,
561 struct nss_cmn_msg *ncm)
562{
563 struct profile_io *pio = (struct profile_io *) ncm->app_data;
564 struct profile_common *pnc = &pio->pnc;
565
566#ifndef __aarch64__
567 counter_rate_by_uint32(pnc);
568#else
569 static uint32_t prev_cnts[32];
570 static uint32_t last_uclk;
571 uint32_t ubi32_freq;
572 uint32_t uclk;
573 int n = pnc->un.num_counters;
574
575 ubi32_freq = htonl(pnc->un.cpu_freq);
576 uclk = pnc->un.rate - last_uclk;
577 last_uclk = pnc->un.rate;
578 printk("%d nss counters: clk dif %u freq %u\n", n, uclk, ubi32_freq);
579 while (n--) {
580 uint32_t v = ntohl(pnc->un.counters[n].value);
581 uint32_t pv = prev_cnts[n];
582
583 prev_cnts[n] = v;
584
585 printk("%-32s 0x%08X %10u : %llu/s\n",
586 pnc->un.counters[n].name, v, v,
587 (uint64_t)(v - pv) * ubi32_freq / uclk);
588 }
589#endif
590}
591
592/*
593 * parseDbgCmd()
594 * process debugging command(s).
595 *
596 * Currently supported command:
597 * "=show-nss-counter" display all values of nss variables registered
598 * by profile_register_performance_counter(&v, name)
599 */
600#define SHOW_COUNTER_CMD "show-nss-counter"
601static int parseDbgCmd(const char *buf, size_t count,
602 struct debug_box *db, struct profile_io *pio)
603{
604 int result;
605
606 if (strncmp(buf, SHOW_COUNTER_CMD, min(sizeof(SHOW_COUNTER_CMD)-1, count))) {
607 printk(KERN_ERR "%s: unsupported cmd %s %zu\n",
608 __func__, buf, strlen(buf));
609 return -EINVAL;
610 }
611
612 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_COUNTERS_MSG;
613 result = nss_profiler_if_tx_buf(pio->ctx, &pio->pnc.un,
614 sizeof(pio->pnc.un),
615 profiler_handle_counter_event_reply, pio);
616 profileInfo("%s: %d\n", __func__, result);
617 return result == NSS_TX_SUCCESS ? count : -EFAULT;
618}
619
Guojun Jin3deae8c2016-08-23 15:51:21 -0700620/*
621 * profiler_handle_stat_event_reply()
622 * print current FW stat event counter configurations
623 */
624static void profiler_handle_stat_event_reply(struct nss_ctx_instance *nss_ctx,
625 struct nss_cmn_msg *ncm)
626{
627 struct profile_io *pio = (struct profile_io *) ncm->app_data;
628 struct debug_box *pdb = (struct debug_box *) &pio->pnc;
629 struct debug_box *db = (struct debug_box *) &ncm[1];
630 int i, thrds;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800631
Guojun Jin3deae8c2016-08-23 15:51:21 -0700632 for (i = 0; i < db->dlen; i++)
633 printk("stat counter %d: %x\n", i, db->data[i]);
634
635 thrds = db->data[i];
636 i = (1 << PROFILE_STS_EVENT_THREAD_BITS) - 1;
637 profileInfo("%d: event end mark %x, ThrA %d ThrB %d\n",
638 ncm->len, thrds, (thrds & i) + 1,
639 ((thrds >> PROFILE_STS_EVENT_THREAD_BITS) & i) + 1);
640
641 /*
642 * save data for read()
643 */
644 memcpy(pdb->data, db->data, (db->dlen + 1) * sizeof(db->data[0]));
645}
646
647/*
648 * parse_sys_stat_event_req()
649 * process FW stat events request: event#1 index#1 event#2 index#2 ...
650 */
651static int parse_sys_stat_event_req(const char *buf, size_t count,
652 struct debug_box *db, struct profile_io *pio)
653{
654 char *cp;
Guojun Jin3eb8dbc2016-10-10 12:42:46 -0700655 int result;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700656
Guojun Jin76cf1392017-05-02 12:02:31 -0700657 printk("%zd cmd buf %s\n", count, buf);
Guojun Jin3deae8c2016-08-23 15:51:21 -0700658 if (count < 19) /* minimum data for sys_stat_event request */
659 return -EINVAL;
660
Guojun Jina09b8b02018-01-25 16:34:43 -0800661 if (strcmp(buf, "get-sys-stat-events") == 0) {
Guojun Jin3deae8c2016-08-23 15:51:21 -0700662 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_GET_SYS_STAT_EVENT;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700663 result = nss_profiler_if_tx_buf(pio->ctx, &pio->pnc.un,
664 sizeof(pio->pnc.un),
665 profiler_handle_stat_event_reply, pio);
666 profileInfo("get_sys_stat_events: %d\n", result);
667 return result == NSS_TX_SUCCESS ? count : -EFAULT;
668 }
669
670 if (strncmp(buf, "set-sys-stat-events", 19)) {
671 printk("unknow event: %s\n", buf);
672 return -EINVAL;
673 }
674
675 db->dlen = sizeof(pio->pnc.un);
676 memset(db->data, 0, PROFILE_STS_EVENT_COUNTERS * sizeof(db->data[0]));
677
678 cp = strchr(buf, ' ');
679 if (!cp) {
680 printk("no enough paramters %s\n", buf);
681 return -EINVAL;
682 }
683
684 do {
685 int idx, event;
686
687 while (isspace(*cp))
688 cp++;
Arunkumar Tcb643c82016-10-14 19:04:09 +0530689 if (kstrtoul(cp, 0, (unsigned long *)&event))
Guojun Jin3eb8dbc2016-10-10 12:42:46 -0700690 return -EINVAL;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700691
692 cp = strchr(cp, ' ');
693 if (!cp) {
694 printk("missing index %s\n", buf);
695 return -EINVAL;
696 }
697 while (isspace(*cp))
698 cp++;
699 idx = event >> 16;
700 if (idx) {
701 if ((event & 0x1FF) < 50) {
702 printk("thr ID (%d) ignored for event %d\n",
703 idx, event & 0x1FF);
704 } else if (idx > 12) {
705 if ((idx >>= 5) > 12) {
706 printk("tID %d too big [1..12]\n", idx);
707 return -E2BIG;
708 }
709 }
710 }
Guojun Jin3eb8dbc2016-10-10 12:42:46 -0700711
Arunkumar Tcb643c82016-10-14 19:04:09 +0530712 if (kstrtoul(cp, 10, (unsigned long *)&idx) || idx < 0 || idx > 7) {
Guojun Jin3deae8c2016-08-23 15:51:21 -0700713 printk("index %d out of range [0..7]\n", idx);
714 return -ERANGE;
715 }
716 printk("%p: e %d i %d\n", db, event, idx);
717 db->data[idx] = event;
718 cp = strchr(cp, ' ');
719 } while (cp);
720 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_SET_SYS_STAT_EVENT;
721 result = nss_profiler_if_tx_buf(pio->ctx, &pio->pnc.un, sizeof(pio->pnc.un),
722 profiler_handle_stat_event_reply, pio);
Guojun Jin76cf1392017-05-02 12:02:31 -0700723 profileInfo("%p: %zd send cmd %x to FW ret %d\n",
Guojun Jin3deae8c2016-08-23 15:51:21 -0700724 db, count, db->hd_magic, result);
725 return count;
726}
727
728/*
729 * parseDbgData()
730 * parsing debug requests: base_address [options] cmd length
731 *
732 * cmd is either read or write
733 * option is one of mio, moveio, h [heap security verify], etc.
734 */
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800735static int parseDbgData(const char *buf, size_t count, struct debug_box *db)
736{
737 char *cp;
738 int n;
739
Guojun Jina09b8b02018-01-25 16:34:43 -0800740 printk("%p %p: buf (%s) cnt %zd\n", db, buf, buf, count);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800741 if (sscanf(buf, "%x", (uint32_t *)&db->base_addr) != 1) {
742 printk("%s: cannot get base addr\n", __func__);
743 return -EINVAL;
744 }
745
746 cp = strchr(buf, ' ');
747 if (!cp) {
748noea: printk("%s: no enough arguments\n", __func__);
749 return -EFAULT;
750 }
751
752 while (isspace(*cp)) cp++;
753 if (!strncmp(cp, "mio", 3) || !strncmp(cp, "moveio", 6)) {
754 printk("%p: cp (%s)\n", cp, cp);
755 cp = strchr(cp, ' ');
756 if (!cp) {
757 goto noea;
758 }
759 db->opts |= DEBUG_OPT_MOVEIO;
760 }
761
762 while (isspace(*cp)) cp++;
Guojun Jina09b8b02018-01-25 16:34:43 -0800763 printk("base addr %X -- %s", db->base_addr, cp);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800764
765 if (!strncmp(cp, "read", 4)) {
766 cp = strchr(cp, ' ');
767 if (cp) {
768 while (isspace(*cp)) cp++;
769 sscanf(cp, "%x", &db->dlen);
770 }
771 return 0;
772 }
773
774 n = 0;
775 do {
776 while (isspace(*cp)) cp++;
777 if (sscanf(cp, "%x", db->data+n) != 1) {
778 printk("n %d : %s\n", n, cp);
779 break;
780 }
Guojun Jin76cf1392017-05-02 12:02:31 -0700781 printk("write %x to off %x\n", db->data[n], n * (int)sizeof(db->data[0]));
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800782 n++;
783 cp = strchr(cp, ' ');
784 } while (cp && n < MAX_DB_WR);
785 return n;
786}
787
788/*
Guojun Jina09b8b02018-01-25 16:34:43 -0800789 * debug_if_show
790 * display memory content read from Phy addr
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800791 */
792static void debug_if_show(struct debug_box *db, int buf_len)
793{
794 int i;
795
796 for (i=0; i < db->dlen; i++) {
797 if ((i & 3) == 0)
Guojun Jina09b8b02018-01-25 16:34:43 -0800798 printk("\n%zX: ", db->base_addr + i * sizeof(db->base_addr));
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800799 printk("%9x", db->data[i]);
800 }
801 printk("\ndumped %d (extra 1) blen %d\n", db->dlen, buf_len);
802}
803
804/*
Guojun Jina09b8b02018-01-25 16:34:43 -0800805 * profiler_handle_debug_reply
806 * show debug message we requested from NSS
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800807 */
808static void profiler_handle_debug_reply(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm)
809{
810 debug_if_show((struct debug_box*)&ncm[1], ncm->len);
811}
812
813/*
Guojun Jina09b8b02018-01-25 16:34:43 -0800814 * debug_if
815 * a generic Krait <--> NSS debug interface
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800816 */
Guojun Jine4fec2f2017-05-16 17:14:23 -0700817static ssize_t debug_if(struct file *filp,
818 const char __user *ubuf, size_t count, loff_t *f_pos)
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800819{
Guojun Jine4fec2f2017-05-16 17:14:23 -0700820 char *buf;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800821 int result;
822 struct debug_box *db;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700823 struct profile_io *pio = (struct profile_io *)filp->private_data;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800824
825 if (!pio) {
826 return -ENOENT;
827 }
828
829 if (!pio->pnc.enabled) {
830 return -EPERM;
831 }
832
Guojun Jine4fec2f2017-05-16 17:14:23 -0700833 buf = kmalloc(count, GFP_KERNEL);
834 if (!buf)
835 return -ENOMEM;
836
837 if (copy_from_user(buf, ubuf, count)) {
838 kfree(buf);
839 printk(KERN_ERR "copy_from_user\n");
840 return -EIO;
841 }
Guojun Jina09b8b02018-01-25 16:34:43 -0800842 buf[count-1] = 0;
Guojun Jine4fec2f2017-05-16 17:14:23 -0700843
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800844 db = (struct debug_box *) &pio->pnc;
845 db->dlen = db->opts = 0;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700846
Guojun Jina09b8b02018-01-25 16:34:43 -0800847 /*
848 * process possible commands
849 */
850 if (buf[0] == '=') {
851 result = parseDbgCmd(buf+1, count, db, pio);
852 kfree(buf);
853 return result;
854 }
855
856 /*
857 * process stat_event request: display/change
858 */
Guojun Jin3deae8c2016-08-23 15:51:21 -0700859 if (!isdigit(buf[0])) {
860 result = parse_sys_stat_event_req(buf, count, db, pio);
Guojun Jine4fec2f2017-05-16 17:14:23 -0700861 kfree(buf);
Guojun Jin3deae8c2016-08-23 15:51:21 -0700862
863 if ((result > 0) && (filp->f_flags & O_RDWR)) {
864 /*
865 * set flag so event-counter can read the data from FW
866 */
867 pio->sw_ksp_ptr = (uint32_t *)db;
868 }
869 return result;
870 }
871
Guojun Jina09b8b02018-01-25 16:34:43 -0800872 /*
873 * process memory I/O for debug
874 */
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800875 result = parseDbgData(buf, count, db);
Guojun Jine4fec2f2017-05-16 17:14:23 -0700876 kfree(buf);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800877 if (result < 0) {
878 return result;
879 }
880
881 if (!result) {
882 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_DEBUG_RD_MSG;
883 } else {
884 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_DEBUG_WR_MSG;
885 db->dlen = result;
886 }
Guojun Jin3deae8c2016-08-23 15:51:21 -0700887 result = nss_profiler_if_tx_buf(pio->ctx, &pio->pnc.un,
888 sizeof(pio->pnc.un), profiler_handle_debug_reply, pio);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800889 printk("dbg res %d dlen = %d opt %x\n", result, db->dlen, db->opts);
890 return count;
891}
892
893static const struct file_operations profile_fops = {
894 .open = profile_open,
895 .read = profile_read,
896 .release = profile_release,
897 .write = debug_if,
898};
899
900/*
901 * showing sample status on Linux console
902 */
903static int profile_rate_show(struct seq_file *m, void *v)
904{
905 struct profile_io *pn = node[0];
906 if (pn) {
907 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
908 seq_printf(m, "%d samples per second. %d ultra, %d linux virtual counters. %d dropped samples. %d queued of %d max sampels. %d sent packets.\n",
909 pn->pnc.un.rate, pn->pnc.un.num_counters, profile_num_counters, psc_hd->dropped_samples, psc_hd->count, psc_hd->max_samples, pn->profile_sequence_num);
910 } else {
911 seq_printf(m, "Profiler is not initialized.\n");
912 }
913 return 0;
914}
915
916static int profile_rate_open(struct inode *inode, struct file *filp)
917{
918 return single_open(filp, profile_rate_show, NULL);
919}
920
Guojun Jin76cf1392017-05-02 12:02:31 -0700921static ssize_t profile_rate_write(struct file *filp, const char *buf, size_t len, loff_t *off)
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800922{
923 *off = 0;
924 return 0;
925}
926
927static const struct file_operations profile_rate_fops = {
928 .open = profile_rate_open,
929 .read = seq_read,
930 .llseek = seq_lseek,
931 .release = single_release,
932 .write = profile_rate_write,
933};
934
935/*
Guojun Jina09b8b02018-01-25 16:34:43 -0800936 * hexdump
937 * hex dump for debug
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800938 */
939static void kxdump(void *buf, int len, const char *who)
940{
Guojun Jina09b8b02018-01-25 16:34:43 -0800941 int32_t *ip = (int32_t *) buf;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800942 int lns = len >> 5; /* 32-B each line */
Guojun Jina09b8b02018-01-25 16:34:43 -0800943 if (lns > 8)
944 lns = 8;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800945 printk("%p: kxdump %s: len %d\n", buf, who, len);
946 do {
947 printk("%x %x %x %x %x %x %x %x\n", ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]);
948 ip += 8;
949 } while (lns--);
950}
951
952/*
Guojun Jina09b8b02018-01-25 16:34:43 -0800953 * profiler_magic_verify
954 * check magic # and detect Endian.
955 *
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800956 * negtive return means failure.
957 * return 1 means need to ntoh swap.
958 */
959static int profiler_magic_verify(struct profile_sample_ctrl_header *psc_hd, int buf_len)
960{
961 int swap = 0;
962 if ((psc_hd->hd_magic & UBI32_PROFILE_HD_MMASK) != UBI32_PROFILE_HD_MAGIC) {
963 if ((psc_hd->hd_magic & UBI32_PROFILE_HD_MMASK_REV) != UBI32_PROFILE_HD_MAGIC_REV) {
964 kxdump(psc_hd, buf_len, "bad profile packet");
Guojun Jina09b8b02018-01-25 16:34:43 -0800965 printk("bad profile HD magic 0x%x : %d\n",
966 psc_hd->hd_magic, buf_len);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800967 return -1;
968 }
969 profileDebug("Profile data in different Endian type %x\n", psc_hd->hd_magic);
970 swap = 1;
971 psc_hd->hd_magic = ntohl(psc_hd->hd_magic);
972 }
973 return swap;
974}
975
976/*
Guojun Jina09b8b02018-01-25 16:34:43 -0800977 * profile_handle_nss_data
978 * process profile sample data from NSS
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800979 */
980static void profile_handle_nss_data(void *arg, struct nss_profiler_msg *npm)
981{
982 int buf_len = npm->cm.len;
983 void *buf = &npm->payload;
984 struct profile_io *pn;
985 struct profile_n2h_sample_buf *nsb;
986 struct profile_sample_ctrl_header *psc_hd = (struct profile_sample_ctrl_header *)buf;
987 int ret, wr;
988 int swap = 0; /* only for header and info data, not samples */
989
990 if (buf_len < (sizeof(struct profile_session) - sizeof(struct profile_counter) * (PROFILE_MAX_APP_COUNTERS))) {
991 printk("profile data packet is too small to be useful %d\n", buf_len);
992 return;
993 }
994
995 swap = profiler_magic_verify(psc_hd, buf_len);
996 if (swap < 0) {
997 return;
998 }
999
1000 pn = (struct profile_io *)arg;
Guojun Jina09b8b02018-01-25 16:34:43 -08001001 profileDebug("PN %p CM msg %d len %d\n", pn, npm->cm.type, buf_len);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001002 profileInfo("%s: dlen %d swap %d cmd %x - %d\n", __func__, buf_len, swap, npm->cm.type, (pn->ccl_read - pn->ccl_write) & (CCL_SIZE-1));
1003 //kxdump(buf, buf_len, "process profile packet");
1004
1005 if (npm->cm.type == NSS_PROFILER_FIXED_INFO_MSG) {
1006 struct profile_session *pTx = (struct profile_session *)buf;
1007 if (swap) {
1008 pn->pnc.un.rate = ntohl(pTx->rate);
1009 pn->pnc.un.cpu_id = ntohl(pTx->cpu_id);
1010 pn->pnc.un.cpu_freq = ntohl(pTx->cpu_freq);
1011 pn->pnc.un.ddr_freq = ntohl(pTx->ddr_freq);
Guojun Jin3deae8c2016-08-23 15:51:21 -07001012 pn->pnc.un.num_counters = pTx->num_counters;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001013 } else {
1014 pn->pnc.un = *pTx;
1015 }
1016 memcpy(pn->pnc.un.counters, pTx->counters, pn->pnc.un.num_counters * sizeof(pn->pnc.un.counters[0]));
1017 pn->profile_first_packet = 1;
1018 return;
1019 }
1020
1021 wr = (pn->ccl_write + 1) & (CCL_SIZE-1);
1022 nsb = pn->ccl + wr;
1023 swap = (pn->ccl_read - wr) & (CCL_SIZE-1); /* PROFILER_FLOWCTRL */
1024 if (nsb->mh.md_type != PINGPONG_EMPTY || (swap && swap < 5)) {
1025 if (pn->pnc.enabled > 0) {
1026 pn->pnc.enabled = -1;
1027 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_STOP_MSG;
Guojun Jin3deae8c2016-08-23 15:51:21 -07001028 ret = nss_profiler_if_tx_buf(pn->ctx,
1029 &pn->pnc.un, sizeof(pn->pnc.un),
1030 profiler_handle_reply, pn);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001031 profileWarn("%d temp stop sampling engine %d\n", swap, ret);
1032 }
1033 if (swap < 3) {
1034 profileWarn("w%p.%d: %d no room for new profile samples r%p.%d\n", nsb, wr, swap, pn->ccl+pn->ccl_read, pn->ccl_read);
1035 return; /* -EMSGSIZE */
1036 }
1037 }
1038 pn->ccl_write = wr;
1039
1040 /*
1041 * sampling data -- hdr NBO swap is done at NSS side via SWAPB.
1042 */
1043 memcpy(&nsb->psc_header, buf, buf_len); /* pn->pnc.pn2h->psc_header = *psc_hd; maybe faster, but take more memory */
1044
1045 nsb->mh.md_type = PINGPONG_FULL;
Guojun Jin3deae8c2016-08-23 15:51:21 -07001046
1047 /*
1048 * ask for perf_counters (software counters) update every 32 samples
1049 */
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001050 if (!wr) {
Guojun Jin3deae8c2016-08-23 15:51:21 -07001051 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_COUNTERS_MSG;
1052 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un,
1053 sizeof(pn->pnc.un), profiler_handle_reply, pn);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001054 if (ret == NSS_TX_FAILURE)
Guojun Jin3deae8c2016-08-23 15:51:21 -07001055 printk("req counters Cmd failed %d %d\n", ret, wr);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001056 }
1057 profileInfo("filled %p %p wr %d\n", nsb, nsb->samples, pn->ccl_write);
1058}
1059
1060/*
Guojun Jina09b8b02018-01-25 16:34:43 -08001061 * profiler_handle_reply
1062 * process N2H reply for message we sent to NSS -- currently no action
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001063 */
1064static void profiler_handle_reply(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm)
1065{
1066 switch (ncm->response) {
1067 default:
Sundarajan Srinivasand09d7dd2014-12-10 16:24:21 -08001068 profileWarn("%p: profiler had error response %d\n", nss_ctx, ncm->response);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001069 /*
1070 * fail through -- no plan to do anything yet
1071 */
1072 case NSS_CMN_RESPONSE_ACK:
1073 return;
1074 }
1075}
1076
1077/*
Guojun Jina09b8b02018-01-25 16:34:43 -08001078 * profile_init
1079 * initialize basic profile data structure
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001080 */
1081static void profile_init(struct profile_io *node)
1082{
1083 int n;
1084
1085 memset(&node->pnc, 0, sizeof(node->pnc));
1086 node->ccl_read = 0;
1087 node->ccl_write = -1;
1088 node->pnc.pn2h = node->ccl;
1089 node->pnc.samples = node->ccl->samples;
1090
1091 for (n = 0; n < CCL_SIZE; n++) {
1092 node->ccl[n].mh.md_type = PINGPONG_EMPTY;
1093 node->ccl[n].psc_header.count = 0;
1094 }
1095
1096 /*
Guojun Jin3deae8c2016-08-23 15:51:21 -07001097 * sw_ksp is an array of pointers to struct thread_info,
1098 * the current task executing for each linux virtual processor
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001099 node->sw_ksp_ptr = sw_ksp;
1100 */
Guojun Jin3deae8c2016-08-23 15:51:21 -07001101 node->sw_ksp_ptr = NULL;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001102 node->task_offset = offsetof(struct thread_info, task);
1103 node->pid_offset = offsetof(struct task_struct, tgid);
1104}
1105
1106static struct proc_dir_entry *pdir;
1107
1108/*
Guojun Jina09b8b02018-01-25 16:34:43 -08001109 * netap_profile_release_resource
1110 * init_module cannot call exit_MODULE, so use this wrapper
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001111 */
1112void netap_profile_release_resource(void)
1113{
1114 if (pdir) {
1115 remove_proc_entry("rate", pdir);
1116 remove_proc_entry("data", pdir);
1117 remove_proc_entry("data1", pdir);
1118 }
1119 kfree(node[0]->ccl);
1120 kfree(node[0]);
1121 node[0] = NULL;
1122}
1123
1124/*
Guojun Jina09b8b02018-01-25 16:34:43 -08001125 * netap_profile_init_module
1126 * kernel module entry
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001127 */
1128int __init netap_profile_init_module(void)
1129{
Murat Sezgin3441e772015-10-26 11:55:57 -07001130#ifdef CONFIG_OF
1131 /*
1132 * If the node is not compatible, don't do anything.
1133 */
1134 if (!of_find_node_by_name(NULL, "nss-common")) {
1135 return 0;
1136 }
1137#endif
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001138 /*
1139 * we need N nodes, not one node + N ctx, for N cores
1140 */
1141 node[0] = kmalloc(sizeof(*node[0]) * NSS_MAX_CORES, GFP_KERNEL);
1142 if (!node[0]) {
1143 printk(KERN_INFO "Profiler CTRL kmalloc failed.\n");
1144 return -ENOMEM;
1145 }
1146
1147 node[0]->ccl = kmalloc(sizeof(*node[0]->ccl) * CCL_SIZE * NSS_MAX_CORES, GFP_KERNEL);
1148 if (!node[0]->ccl) {
1149 printk(KERN_INFO "Profiler n2h_sample_buf kmalloc failed.\n");
1150 kfree(node[0]);
1151 node[0] = NULL;
1152 return -ENOMEM;
1153 }
1154
1155 /*
1156 * connect to the file system
1157 */
1158 pdir = proc_mkdir("profile", NULL);
1159 if (!pdir ||
1160 !proc_create("data", 0, pdir, &profile_fops) ||
1161 !proc_create("data1", 0, pdir, &profile_fops) ||
1162 !proc_create("rate", 0, pdir, &profile_rate_fops)) {
1163 netap_profile_release_resource();
1164 return -ENOMEM;
1165 }
1166
1167 profile_init(node[0]);
1168
1169 /*
1170 * attatch the device callback to N2H channel for CPU 0
1171 */
1172 node[0]->ctx = nss_profiler_notify_register(NSS_CORE_0, profile_handle_nss_data, node[0]);
1173#if NSS_MAX_CORES > 1
1174 node[1] = node[0] + 1;
1175 node[1]->ccl = node[0]->ccl + CCL_SIZE;
1176
1177 profile_init(node[1]);
1178 node[1]->ctx = nss_profiler_notify_register(NSS_CORE_1, profile_handle_nss_data, node[1]);
1179 profile_register_performance_counter(&node[1]->profile_sequence_num, "Profile1 DRV data packets");
1180#endif
1181
1182 profile_register_performance_counter(&node[0]->profile_sequence_num, "Profile0 DRV data packets");
1183 return 0;
1184}
1185
1186/*
Guojun Jina09b8b02018-01-25 16:34:43 -08001187 * netap_profile_exit_module
1188 * kernel module exit
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001189 */
1190void __exit netap_profile_exit_module(void)
1191{
Murat Sezgin3441e772015-10-26 11:55:57 -07001192#ifdef CONFIG_OF
1193 /*
1194 * If the node is not compatible, don't do anything.
1195 */
1196 if (!of_find_node_by_name(NULL, "nss-common")) {
1197 return;
1198 }
1199#endif
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001200 nss_profiler_notify_unregister(NSS_CORE_0);
1201#if NSS_MAX_CORES > 1
1202 nss_profiler_notify_unregister(NSS_CORE_1);
1203#endif
1204 netap_profile_release_resource();
1205}
1206
1207module_init(netap_profile_init_module);
1208module_exit(netap_profile_exit_module);
1209
1210MODULE_LICENSE("Dual BSD/GPL");