blob: 53db7f514e41f510b34aa62b947dce63b8306699 [file] [log] [blame]
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001/*
2 **************************************************************************
Guojun Jind3328392016-01-22 14:14:17 -08003 * Copyright (c) 2014,2016 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/*
147 * make a packet full of sample data
148 */
149static int profile_make_data_packet(char *buf, int blen, struct profile_io *pn)
150{
151 int sp_samples = 0; /* separated samples if any */
152 int ns; /* number of samples requested */
153 struct profile_header ph;
154 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
155
156 if (blen < sizeof(ph) + sizeof(struct profile_sample)) {
157 return -EINVAL;
158 }
159
160 profileDebug("%p stat %x cnt %d %p\n", pn->pnc.pn2h, pn->pnc.pn2h->mh.md_type, psc_hd->count, pn->ccl);
161
162 if (pn->pnc.pn2h->mh.md_type == PINGPONG_EMPTY || psc_hd->count < 1) {
163 struct profile_n2h_sample_buf *nsb;
164 ns = (pn->ccl_read + 1) & (CCL_SIZE-1);
165 nsb = pn->ccl + ns;
166 if (ns == pn->ccl_write || nsb->mh.md_type != PINGPONG_FULL) {
167 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);
168 return -EAGAIN;
169 }
170 pn->ccl_read = ns;
171 profileInfo("sp %p => %p rd %d %p\n", pn->pnc.samples, nsb->samples, ns, nsb);
172 psc_hd = &nsb->psc_header;
173 pn->pnc.pn2h = nsb;
174 pn->pnc.samples = nsb->samples;
175 pn->pnc.cur = 0;
176 }
177 pn->pnc.pn2h->mh.md_type = PINGPONG_INUSE;
178
179 /*
180 * fill in the packet header
181 */
182 memset(&ph, 0, sizeof(ph));
183 ph.pph.magic = htons(PROF_MAGIC + PROFILE_VERSION);
184 ph.pph.header_size = sizeof(ph);
185 ph.pph.profile_instructions = 0;
186 ph.pph.clock_freq = pn->pnc.un.cpu_freq;
187 ph.pph.ddr_freq = pn->pnc.un.ddr_freq;
188 ph.pph.cpu_id = pn->pnc.un.cpu_id;
189 ph.pph.seq_num = htonl(pn->profile_sequence_num);
Guojun Jin3deae8c2016-08-23 15:51:21 -0700190 ph.pph.sample_stack_words = PROFILE_STACK_WORDS;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800191
192 ns = (blen - sizeof(ph)) / sizeof(struct profile_sample);
193 profileInfo("%X: blen %d ns = %d psc_hd count %d ssets %d phs %d pss %d\n", pn->profile_sequence_num, blen, ns, psc_hd->count, psc_hd->exh.sample_sets, sizeof(ph), sizeof(struct profile_sample));
194 if (ns > psc_hd->count)
195 ns = psc_hd->count;
196 if (ns == 0) {
197 printk("NS should not be 0: rlen %d hd cnt %d\n", blen, psc_hd->count);
198 return 0;
199 }
200
201 /*
202 * if buf cannot hold all samples, then samples must be separated by set.
203 */
204 if (ns < psc_hd->count) {
205 ph.exh.sets_map = psc_hd->exh.sets_map; /* save for separating sets */
206 do {
207 sp_samples += psc_hd->exh.sets_map & 0x0F;
208 psc_hd->exh.sets_map >>= 4; /* remove the last set */
209 psc_hd->exh.sample_sets--;
210 ph.exh.sample_sets++; /* save for restore later */
211 } while ((psc_hd->count - sp_samples) > ns);
212 ns = psc_hd->count - sp_samples;
213 }
214 ph.pph.sample_count = ns;
215 if (copy_to_user(buf, &ph.pph, sizeof(ph.pph)) != 0) {
216 return -EFAULT;
217 }
218 buf += sizeof(ph.pph);
219
220 /*
221 * ph.exh is unused dummy; and psc_hd->exh is used directly to avoid double mem copy
222 */
223 if (copy_to_user(buf, &psc_hd->exh, sizeof(psc_hd->exh)) != 0) {
224 return -EFAULT;
225 }
226 buf += sizeof(psc_hd->exh);
227
228 blen = ns * sizeof(struct profile_sample);
229 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));
230 if (copy_to_user(buf, &pn->pnc.samples[pn->pnc.cur], blen) != 0) {
231 return -EFAULT;
232 }
233 pn->pnc.cur += ns;
234 psc_hd->count -= ns;
235 if (psc_hd->count < 1)
236 pn->pnc.pn2h->mh.md_type = PINGPONG_EMPTY;
237
238 /*
239 * restore left over sample counts; 0s for no one
240 */
241 if (sp_samples) {
242 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);
243 psc_hd->exh.sample_sets = ph.exh.sample_sets;
244 psc_hd->exh.sets_map = ph.exh.sets_map;
245 }
246
247 pn->profile_sequence_num++;
248 blen += sizeof(ph);
249 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);
250 return blen;
251}
252
253/*
254 * This is no longer needed due to NetAP and Linux use different CPUs, and profile is NetAP only.
255 * All related code will be removed after corresponging code in visual tool is corrected; otherwise
256 * visual tool will mis-behave
257 */
258struct profile_counter profile_builtin_stats[] =
259{
260 {
261 "Free memory(KB)", 0
262 },
263 {
264 "Max free Block(KB)", 0
265 }
266};
267
268/*
Guojun Jin3deae8c2016-08-23 15:51:21 -0700269 * make a packet full of performance counters (software)
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800270 */
271static int profile_make_stats_packet(char *buf, int bytes, struct profile_io *pn)
272{
273 static char prof_pkt[PROFILE_MAX_PACKET_SIZE];
274
275 char *ptr;
276 int n;
277 struct profile_counter *counter_ptr;
278 struct profile_header_counters *hdr = (struct profile_header_counters *)prof_pkt;
279 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
280
281 if (bytes > PROFILE_MAX_PACKET_SIZE) {
282 bytes = PROFILE_MAX_PACKET_SIZE;
283 }
284 n = sizeof(profile_builtin_stats) + (pn->pnc.un.num_counters + profile_num_counters) * sizeof(*counter_ptr);
285
286 if ((bytes - sizeof(hdr)) < n) {
287 profileWarn("room too small %d for cnts %d\n", bytes, n);
288 return 0;
289 }
290
291 hdr->magic = htons(PROF_MAGIC_COUNTERS);
292 hdr->ultra_count = htons(pn->pnc.un.num_counters);
293 hdr->linux_count = htonl(profile_num_counters + sizeof(profile_builtin_stats) / sizeof(*counter_ptr));
294 hdr->ultra_sample_time = psc_hd->exh.clocks;
295 hdr->linux_sample_time = psc_hd->exh.clocks; /* QSDK has no time func */
296
297 n = pn->pnc.un.num_counters; /* copy NSS counters */
298 n *= sizeof(pn->pnc.un.counters[0]);
299 ptr = (char*) (hdr + 1);
300 memcpy(ptr, (void *)(pn->pnc.un.counters), n);
301 ptr += n;
302
303 counter_ptr = (struct profile_counter *)ptr;
304 for (n = 0; n < profile_num_counters; ++n) {
305 counter_ptr->value = htonl(*profile_counter[n]);
Guojun Jind3328392016-01-22 14:14:17 -0800306 strlcpy(counter_ptr->name, profile_name[n],
307 PROFILE_COUNTER_NAME_LENGTH);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800308 counter_ptr++;
309 }
310 ptr = (char*)counter_ptr;
311
312 /*
313 * built in statistics
314 profile_get_memory_stats(&total_free, &max_free);
315 */
316 profile_builtin_stats[0].value = 0;
317 profile_builtin_stats[1].value = 0;
318 memcpy(ptr, (void *)profile_builtin_stats, sizeof(profile_builtin_stats));
319 ptr += sizeof(profile_builtin_stats);
320
321 n = ptr - prof_pkt;
322 if (copy_to_user(buf, prof_pkt, n) != 0) {
323 return -EFAULT;
324 }
325 return n;
326}
327
328/*
329 * space for all memory blocks so we can hold locks for short time when walking tables
330 */
331static struct profile_io *node[NSS_MAX_CORES];
332
333static int profile_open(struct inode *inode, struct file *filp)
334{
335 int n;
336 struct profile_io *pn;
337
338 if (filp->private_data)
Shashank Balashankar0b352e62016-03-29 15:48:36 -0700339 printk(KERN_WARNING "%s: %p\n", filp->f_path.dentry->d_iname, filp->private_data);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800340
Shashank Balashankar0b352e62016-03-29 15:48:36 -0700341 n = filp->f_path.dentry->d_iname[strlen(filp->f_path.dentry->d_iname) - 1] - '0';
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800342 if (n < 0 || n >= NSS_MAX_CORES)
343 n = 0;
344 pn = node[n];
345 if (!pn) {
346 return -ENOENT;
347 }
348
349 if (!pn->pnc.enabled && nss_get_state(pn->ctx) == NSS_STATE_INITIALIZED) {
350 nss_tx_status_t ret;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700351
352 /*
353 * sw_ksp_ptr is used as event flag. NULL means normal I/O
354 */
355 pn->sw_ksp_ptr = NULL;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800356 pn->pnc.enabled = 1;
357 pn->profile_first_packet = 1;
358 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_START_MSG;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700359 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un,
360 sizeof(pn->pnc.un), profiler_handle_reply, pn);
361 profileInfo("%s: %d -- %p: ccl %p sp %p\n", __func__, ret,
362 pn, pn->ccl, pn->pnc.samples);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800363 filp->private_data = pn;
364 return 0;
365 }
366
Guojun Jin3deae8c2016-08-23 15:51:21 -0700367 profileWarn("profile ena %d nss stat %x\n", pn->pnc.enabled,
368 nss_get_state(pn->ctx));
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800369 return -EBUSY;
370}
371
372/*
373 * return a udp packet ready to send to the profiler tool
374 * when there are no packets left to make, return 0
375 */
Guojun Jin76cf1392017-05-02 12:02:31 -0700376static ssize_t profile_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800377{
378 int result = 0;
379 int slen = 0;
380 struct profile_io *pn = (struct profile_io *)filp->private_data;
381 if (!pn) {
382 return -ENOENT;
383 }
384
385 if (!pn->pnc.enabled) {
386 return -EPERM;
387 }
Guojun Jin3deae8c2016-08-23 15:51:21 -0700388 if (pn->sw_ksp_ptr) {
389 struct debug_box *db = (struct debug_box *) pn->sw_ksp_ptr;
390 slen = (PROFILE_STS_EVENT_COUNTERS + 1) * sizeof(db->data[0]);
391 if (copy_to_user(buf, db->data, slen))
392 return -EFAULT;
393 return slen;
394 }
395
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800396 if (!pn->pnc.samples) {
397 return -ENOMEM;
398 }
399
400 if (pn->profile_first_packet) {
401 result = profile_make_stats_packet(buf, count, pn);
402 pn->profile_first_packet = 0;
Guojun Jin76cf1392017-05-02 12:02:31 -0700403 profileInfo("%d profile_make_stats_packet %zd\n", result, count);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800404
405#ifdef PROFILE_SEP_STAT
406 /*
407 * currectly, stat and sample data are combined in one pkt for efficient;
408 * but this is harder to debug and required remote tool to handle
409 * packet in all-in-one method instead of individual handler.
410 */
411 return result;
412#endif
413 }
414
415 if (result > 0) {
416 buf += result;
417 count -= result;
418 slen = result;
419 }
420 result = profile_make_data_packet(buf, count, pn);
421 if (result == 0) {
422 pn->profile_first_packet = 1;
423 }
Guojun Jin76cf1392017-05-02 12:02:31 -0700424 profileInfo("%d: profile_make_data_packet %zd %d\n", result, count, slen);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800425
426 profileInfo("%d: read\n", pn->pnc.enabled);
427 if (pn->pnc.enabled < 0) {
428 nss_tx_status_t ret;
429 pn->pnc.enabled = 1;
430 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_START_MSG;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700431 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un, sizeof(pn->pnc.un),
432 profiler_handle_reply, pn);
433 profileWarn("%s: restart %d -- %p: ccl %p sp %p\n", __func__,
434 ret, pn, pn->ccl, pn->pnc.samples);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800435 }
436
437 return result + slen;
438}
439
440/*
441 * the close function paired with profiler_open
442 */
443static int profile_release(struct inode *inode, struct file *filp)
444{
445 struct profile_io *pn = (struct profile_io *)filp->private_data;
446 if (!pn) {
447 return -ENOENT;
448 }
449
450 if (pn->pnc.enabled) {
451 nss_tx_status_t ret;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700452 pn->sw_ksp_ptr = NULL;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800453 pn->pnc.enabled = 0;
454 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_STOP_MSG;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700455 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un,
456 sizeof(pn->pnc.un), profiler_handle_reply, pn);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800457 profileInfo("%s: %p %d\n", __func__, pn, ret);
458 return 0;
459 }
460 profileWarn("%s: attempt closing non-open dev %p\n", __func__, pn);
461 pn->profile_first_packet = 1;
462 return -EBADF;
463}
464
Guojun Jin3deae8c2016-08-23 15:51:21 -0700465/*
466 * profiler_handle_stat_event_reply()
467 * print current FW stat event counter configurations
468 */
469static void profiler_handle_stat_event_reply(struct nss_ctx_instance *nss_ctx,
470 struct nss_cmn_msg *ncm)
471{
472 struct profile_io *pio = (struct profile_io *) ncm->app_data;
473 struct debug_box *pdb = (struct debug_box *) &pio->pnc;
474 struct debug_box *db = (struct debug_box *) &ncm[1];
475 int i, thrds;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800476
Guojun Jin3deae8c2016-08-23 15:51:21 -0700477 for (i = 0; i < db->dlen; i++)
478 printk("stat counter %d: %x\n", i, db->data[i]);
479
480 thrds = db->data[i];
481 i = (1 << PROFILE_STS_EVENT_THREAD_BITS) - 1;
482 profileInfo("%d: event end mark %x, ThrA %d ThrB %d\n",
483 ncm->len, thrds, (thrds & i) + 1,
484 ((thrds >> PROFILE_STS_EVENT_THREAD_BITS) & i) + 1);
485
486 /*
487 * save data for read()
488 */
489 memcpy(pdb->data, db->data, (db->dlen + 1) * sizeof(db->data[0]));
490}
491
492/*
493 * parse_sys_stat_event_req()
494 * process FW stat events request: event#1 index#1 event#2 index#2 ...
495 */
496static int parse_sys_stat_event_req(const char *buf, size_t count,
497 struct debug_box *db, struct profile_io *pio)
498{
499 char *cp;
Guojun Jin3eb8dbc2016-10-10 12:42:46 -0700500 int result;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700501
Guojun Jin76cf1392017-05-02 12:02:31 -0700502 printk("%zd cmd buf %s\n", count, buf);
Guojun Jin3deae8c2016-08-23 15:51:21 -0700503 if (count < 19) /* minimum data for sys_stat_event request */
504 return -EINVAL;
505
506 if (strncmp(buf, "get-sys-stat-events", 19) == 0) {
507 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_GET_SYS_STAT_EVENT;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700508 result = nss_profiler_if_tx_buf(pio->ctx, &pio->pnc.un,
509 sizeof(pio->pnc.un),
510 profiler_handle_stat_event_reply, pio);
511 profileInfo("get_sys_stat_events: %d\n", result);
512 return result == NSS_TX_SUCCESS ? count : -EFAULT;
513 }
514
515 if (strncmp(buf, "set-sys-stat-events", 19)) {
516 printk("unknow event: %s\n", buf);
517 return -EINVAL;
518 }
519
520 db->dlen = sizeof(pio->pnc.un);
521 memset(db->data, 0, PROFILE_STS_EVENT_COUNTERS * sizeof(db->data[0]));
522
523 cp = strchr(buf, ' ');
524 if (!cp) {
525 printk("no enough paramters %s\n", buf);
526 return -EINVAL;
527 }
528
529 do {
530 int idx, event;
531
532 while (isspace(*cp))
533 cp++;
Arunkumar Tcb643c82016-10-14 19:04:09 +0530534 if (kstrtoul(cp, 0, (unsigned long *)&event))
Guojun Jin3eb8dbc2016-10-10 12:42:46 -0700535 return -EINVAL;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700536
537 cp = strchr(cp, ' ');
538 if (!cp) {
539 printk("missing index %s\n", buf);
540 return -EINVAL;
541 }
542 while (isspace(*cp))
543 cp++;
544 idx = event >> 16;
545 if (idx) {
546 if ((event & 0x1FF) < 50) {
547 printk("thr ID (%d) ignored for event %d\n",
548 idx, event & 0x1FF);
549 } else if (idx > 12) {
550 if ((idx >>= 5) > 12) {
551 printk("tID %d too big [1..12]\n", idx);
552 return -E2BIG;
553 }
554 }
555 }
Guojun Jin3eb8dbc2016-10-10 12:42:46 -0700556
Arunkumar Tcb643c82016-10-14 19:04:09 +0530557 if (kstrtoul(cp, 10, (unsigned long *)&idx) || idx < 0 || idx > 7) {
Guojun Jin3deae8c2016-08-23 15:51:21 -0700558 printk("index %d out of range [0..7]\n", idx);
559 return -ERANGE;
560 }
561 printk("%p: e %d i %d\n", db, event, idx);
562 db->data[idx] = event;
563 cp = strchr(cp, ' ');
564 } while (cp);
565 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_SET_SYS_STAT_EVENT;
566 result = nss_profiler_if_tx_buf(pio->ctx, &pio->pnc.un, sizeof(pio->pnc.un),
567 profiler_handle_stat_event_reply, pio);
Guojun Jin76cf1392017-05-02 12:02:31 -0700568 profileInfo("%p: %zd send cmd %x to FW ret %d\n",
Guojun Jin3deae8c2016-08-23 15:51:21 -0700569 db, count, db->hd_magic, result);
570 return count;
571}
572
573/*
574 * parseDbgData()
575 * parsing debug requests: base_address [options] cmd length
576 *
577 * cmd is either read or write
578 * option is one of mio, moveio, h [heap security verify], etc.
579 */
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800580static int parseDbgData(const char *buf, size_t count, struct debug_box *db)
581{
582 char *cp;
583 int n;
584
Guojun Jin76cf1392017-05-02 12:02:31 -0700585 printk("%p: buf (%s) cnt %zd\n", buf, buf, count);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800586 if (sscanf(buf, "%x", (uint32_t *)&db->base_addr) != 1) {
587 printk("%s: cannot get base addr\n", __func__);
588 return -EINVAL;
589 }
590
591 cp = strchr(buf, ' ');
592 if (!cp) {
593noea: printk("%s: no enough arguments\n", __func__);
594 return -EFAULT;
595 }
596
597 while (isspace(*cp)) cp++;
598 if (!strncmp(cp, "mio", 3) || !strncmp(cp, "moveio", 6)) {
599 printk("%p: cp (%s)\n", cp, cp);
600 cp = strchr(cp, ' ');
601 if (!cp) {
602 goto noea;
603 }
604 db->opts |= DEBUG_OPT_MOVEIO;
605 }
606
607 while (isspace(*cp)) cp++;
608 printk("base addr %p -- %s\n", db->base_addr, cp);
609
610 if (!strncmp(cp, "read", 4)) {
611 cp = strchr(cp, ' ');
612 if (cp) {
613 while (isspace(*cp)) cp++;
614 sscanf(cp, "%x", &db->dlen);
615 }
616 return 0;
617 }
618
619 n = 0;
620 do {
621 while (isspace(*cp)) cp++;
622 if (sscanf(cp, "%x", db->data+n) != 1) {
623 printk("n %d : %s\n", n, cp);
624 break;
625 }
Guojun Jin76cf1392017-05-02 12:02:31 -0700626 printk("write %x to off %x\n", db->data[n], n * (int)sizeof(db->data[0]));
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800627 n++;
628 cp = strchr(cp, ' ');
629 } while (cp && n < MAX_DB_WR);
630 return n;
631}
632
633/*
634 * display memory content read from Phy addr
635 */
636static void debug_if_show(struct debug_box *db, int buf_len)
637{
638 int i;
639
640 for (i=0; i < db->dlen; i++) {
641 if ((i & 3) == 0)
642 printk("\n%p: ", db->base_addr + i);
643 printk("%9x", db->data[i]);
644 }
645 printk("\ndumped %d (extra 1) blen %d\n", db->dlen, buf_len);
646}
647
648/*
649 * show debug message we requested from NSS
650 */
651static void profiler_handle_debug_reply(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm)
652{
653 debug_if_show((struct debug_box*)&ncm[1], ncm->len);
654}
655
656/*
657 * a generic Krait <--> NSS debug interface
658 */
Guojun Jin76cf1392017-05-02 12:02:31 -0700659static ssize_t debug_if(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800660{
661 int result;
662 struct debug_box *db;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700663 struct profile_io *pio = (struct profile_io *)filp->private_data;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800664
665 if (!pio) {
666 return -ENOENT;
667 }
668
669 if (!pio->pnc.enabled) {
670 return -EPERM;
671 }
672
673 db = (struct debug_box *) &pio->pnc;
674 db->dlen = db->opts = 0;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700675
676 if (!isdigit(buf[0])) {
677 result = parse_sys_stat_event_req(buf, count, db, pio);
678
679 if ((result > 0) && (filp->f_flags & O_RDWR)) {
680 /*
681 * set flag so event-counter can read the data from FW
682 */
683 pio->sw_ksp_ptr = (uint32_t *)db;
684 }
685 return result;
686 }
687
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800688 result = parseDbgData(buf, count, db);
689 if (result < 0) {
690 return result;
691 }
692
693 if (!result) {
694 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_DEBUG_RD_MSG;
695 } else {
696 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_DEBUG_WR_MSG;
697 db->dlen = result;
698 }
Guojun Jin3deae8c2016-08-23 15:51:21 -0700699 result = nss_profiler_if_tx_buf(pio->ctx, &pio->pnc.un,
700 sizeof(pio->pnc.un), profiler_handle_debug_reply, pio);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800701 printk("dbg res %d dlen = %d opt %x\n", result, db->dlen, db->opts);
702 return count;
703}
704
705static const struct file_operations profile_fops = {
706 .open = profile_open,
707 .read = profile_read,
708 .release = profile_release,
709 .write = debug_if,
710};
711
712/*
713 * showing sample status on Linux console
714 */
715static int profile_rate_show(struct seq_file *m, void *v)
716{
717 struct profile_io *pn = node[0];
718 if (pn) {
719 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
720 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",
721 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);
722 } else {
723 seq_printf(m, "Profiler is not initialized.\n");
724 }
725 return 0;
726}
727
728static int profile_rate_open(struct inode *inode, struct file *filp)
729{
730 return single_open(filp, profile_rate_show, NULL);
731}
732
Guojun Jin76cf1392017-05-02 12:02:31 -0700733static ssize_t profile_rate_write(struct file *filp, const char *buf, size_t len, loff_t *off)
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800734{
735 *off = 0;
736 return 0;
737}
738
739static const struct file_operations profile_rate_fops = {
740 .open = profile_rate_open,
741 .read = seq_read,
742 .llseek = seq_lseek,
743 .release = single_release,
744 .write = profile_rate_write,
745};
746
747/*
748 * hex dump for debug
749 */
750static void kxdump(void *buf, int len, const char *who)
751{
752 int *ip = (int*) buf;
753 int lns = len >> 5; /* 32-B each line */
754 if (lns > 4)
755 lns = 4;
756 printk("%p: kxdump %s: len %d\n", buf, who, len);
757 do {
758 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]);
759 ip += 8;
760 } while (lns--);
761}
762
763/*
764 * check magic # and detect Endian.
765 * negtive return means failure.
766 * return 1 means need to ntoh swap.
767 */
768static int profiler_magic_verify(struct profile_sample_ctrl_header *psc_hd, int buf_len)
769{
770 int swap = 0;
771 if ((psc_hd->hd_magic & UBI32_PROFILE_HD_MMASK) != UBI32_PROFILE_HD_MAGIC) {
772 if ((psc_hd->hd_magic & UBI32_PROFILE_HD_MMASK_REV) != UBI32_PROFILE_HD_MAGIC_REV) {
773 kxdump(psc_hd, buf_len, "bad profile packet");
774 printk("bad profile packet %x : %d\n", psc_hd->hd_magic, buf_len);
775 return -1;
776 }
777 profileDebug("Profile data in different Endian type %x\n", psc_hd->hd_magic);
778 swap = 1;
779 psc_hd->hd_magic = ntohl(psc_hd->hd_magic);
780 }
781 return swap;
782}
783
784/*
785 * process profile sample data from NSS
786 */
787static void profile_handle_nss_data(void *arg, struct nss_profiler_msg *npm)
788{
789 int buf_len = npm->cm.len;
790 void *buf = &npm->payload;
791 struct profile_io *pn;
792 struct profile_n2h_sample_buf *nsb;
793 struct profile_sample_ctrl_header *psc_hd = (struct profile_sample_ctrl_header *)buf;
794 int ret, wr;
795 int swap = 0; /* only for header and info data, not samples */
796
797 if (buf_len < (sizeof(struct profile_session) - sizeof(struct profile_counter) * (PROFILE_MAX_APP_COUNTERS))) {
798 printk("profile data packet is too small to be useful %d\n", buf_len);
799 return;
800 }
801
802 swap = profiler_magic_verify(psc_hd, buf_len);
803 if (swap < 0) {
804 return;
805 }
806
807 pn = (struct profile_io *)arg;
808 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));
809 //kxdump(buf, buf_len, "process profile packet");
810
811 if (npm->cm.type == NSS_PROFILER_FIXED_INFO_MSG) {
812 struct profile_session *pTx = (struct profile_session *)buf;
813 if (swap) {
814 pn->pnc.un.rate = ntohl(pTx->rate);
815 pn->pnc.un.cpu_id = ntohl(pTx->cpu_id);
816 pn->pnc.un.cpu_freq = ntohl(pTx->cpu_freq);
817 pn->pnc.un.ddr_freq = ntohl(pTx->ddr_freq);
Guojun Jin3deae8c2016-08-23 15:51:21 -0700818 pn->pnc.un.num_counters = pTx->num_counters;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800819 } else {
820 pn->pnc.un = *pTx;
821 }
822 memcpy(pn->pnc.un.counters, pTx->counters, pn->pnc.un.num_counters * sizeof(pn->pnc.un.counters[0]));
823 pn->profile_first_packet = 1;
824 return;
825 }
826
827 wr = (pn->ccl_write + 1) & (CCL_SIZE-1);
828 nsb = pn->ccl + wr;
829 swap = (pn->ccl_read - wr) & (CCL_SIZE-1); /* PROFILER_FLOWCTRL */
830 if (nsb->mh.md_type != PINGPONG_EMPTY || (swap && swap < 5)) {
831 if (pn->pnc.enabled > 0) {
832 pn->pnc.enabled = -1;
833 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_STOP_MSG;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700834 ret = nss_profiler_if_tx_buf(pn->ctx,
835 &pn->pnc.un, sizeof(pn->pnc.un),
836 profiler_handle_reply, pn);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800837 profileWarn("%d temp stop sampling engine %d\n", swap, ret);
838 }
839 if (swap < 3) {
840 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);
841 return; /* -EMSGSIZE */
842 }
843 }
844 pn->ccl_write = wr;
845
846 /*
847 * sampling data -- hdr NBO swap is done at NSS side via SWAPB.
848 */
849 memcpy(&nsb->psc_header, buf, buf_len); /* pn->pnc.pn2h->psc_header = *psc_hd; maybe faster, but take more memory */
850
851 nsb->mh.md_type = PINGPONG_FULL;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700852
853 /*
854 * ask for perf_counters (software counters) update every 32 samples
855 */
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800856 if (!wr) {
Guojun Jin3deae8c2016-08-23 15:51:21 -0700857 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_COUNTERS_MSG;
858 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un,
859 sizeof(pn->pnc.un), profiler_handle_reply, pn);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800860 if (ret == NSS_TX_FAILURE)
Guojun Jin3deae8c2016-08-23 15:51:21 -0700861 printk("req counters Cmd failed %d %d\n", ret, wr);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800862 }
863 profileInfo("filled %p %p wr %d\n", nsb, nsb->samples, pn->ccl_write);
864}
865
866/*
867 * process N2H reply for message we sent to NSS -- currently no action
868 */
869static void profiler_handle_reply(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm)
870{
871 switch (ncm->response) {
872 default:
Sundarajan Srinivasand09d7dd2014-12-10 16:24:21 -0800873 profileWarn("%p: profiler had error response %d\n", nss_ctx, ncm->response);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800874 /*
875 * fail through -- no plan to do anything yet
876 */
877 case NSS_CMN_RESPONSE_ACK:
878 return;
879 }
880}
881
882/*
883 * initialize basic profile data structure
884 */
885static void profile_init(struct profile_io *node)
886{
887 int n;
888
889 memset(&node->pnc, 0, sizeof(node->pnc));
890 node->ccl_read = 0;
891 node->ccl_write = -1;
892 node->pnc.pn2h = node->ccl;
893 node->pnc.samples = node->ccl->samples;
894
895 for (n = 0; n < CCL_SIZE; n++) {
896 node->ccl[n].mh.md_type = PINGPONG_EMPTY;
897 node->ccl[n].psc_header.count = 0;
898 }
899
900 /*
Guojun Jin3deae8c2016-08-23 15:51:21 -0700901 * sw_ksp is an array of pointers to struct thread_info,
902 * the current task executing for each linux virtual processor
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800903 node->sw_ksp_ptr = sw_ksp;
904 */
Guojun Jin3deae8c2016-08-23 15:51:21 -0700905 node->sw_ksp_ptr = NULL;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800906 node->task_offset = offsetof(struct thread_info, task);
907 node->pid_offset = offsetof(struct task_struct, tgid);
908}
909
910static struct proc_dir_entry *pdir;
911
912/*
913 * init_module cannot call exit_MODULE, so use this wrapper
914 */
915void netap_profile_release_resource(void)
916{
917 if (pdir) {
918 remove_proc_entry("rate", pdir);
919 remove_proc_entry("data", pdir);
920 remove_proc_entry("data1", pdir);
921 }
922 kfree(node[0]->ccl);
923 kfree(node[0]);
924 node[0] = NULL;
925}
926
927/*
928 * kernel module entry
929 */
930int __init netap_profile_init_module(void)
931{
Murat Sezgin3441e772015-10-26 11:55:57 -0700932#ifdef CONFIG_OF
933 /*
934 * If the node is not compatible, don't do anything.
935 */
936 if (!of_find_node_by_name(NULL, "nss-common")) {
937 return 0;
938 }
939#endif
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800940 /*
941 * we need N nodes, not one node + N ctx, for N cores
942 */
943 node[0] = kmalloc(sizeof(*node[0]) * NSS_MAX_CORES, GFP_KERNEL);
944 if (!node[0]) {
945 printk(KERN_INFO "Profiler CTRL kmalloc failed.\n");
946 return -ENOMEM;
947 }
948
949 node[0]->ccl = kmalloc(sizeof(*node[0]->ccl) * CCL_SIZE * NSS_MAX_CORES, GFP_KERNEL);
950 if (!node[0]->ccl) {
951 printk(KERN_INFO "Profiler n2h_sample_buf kmalloc failed.\n");
952 kfree(node[0]);
953 node[0] = NULL;
954 return -ENOMEM;
955 }
956
957 /*
958 * connect to the file system
959 */
960 pdir = proc_mkdir("profile", NULL);
961 if (!pdir ||
962 !proc_create("data", 0, pdir, &profile_fops) ||
963 !proc_create("data1", 0, pdir, &profile_fops) ||
964 !proc_create("rate", 0, pdir, &profile_rate_fops)) {
965 netap_profile_release_resource();
966 return -ENOMEM;
967 }
968
969 profile_init(node[0]);
970
971 /*
972 * attatch the device callback to N2H channel for CPU 0
973 */
974 node[0]->ctx = nss_profiler_notify_register(NSS_CORE_0, profile_handle_nss_data, node[0]);
975#if NSS_MAX_CORES > 1
976 node[1] = node[0] + 1;
977 node[1]->ccl = node[0]->ccl + CCL_SIZE;
978
979 profile_init(node[1]);
980 node[1]->ctx = nss_profiler_notify_register(NSS_CORE_1, profile_handle_nss_data, node[1]);
981 profile_register_performance_counter(&node[1]->profile_sequence_num, "Profile1 DRV data packets");
982#endif
983
984 profile_register_performance_counter(&node[0]->profile_sequence_num, "Profile0 DRV data packets");
985 return 0;
986}
987
988/*
989 * kernel module exit
990 */
991void __exit netap_profile_exit_module(void)
992{
Murat Sezgin3441e772015-10-26 11:55:57 -0700993#ifdef CONFIG_OF
994 /*
995 * If the node is not compatible, don't do anything.
996 */
997 if (!of_find_node_by_name(NULL, "nss-common")) {
998 return;
999 }
1000#endif
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001001 nss_profiler_notify_unregister(NSS_CORE_0);
1002#if NSS_MAX_CORES > 1
1003 nss_profiler_notify_unregister(NSS_CORE_1);
1004#endif
1005 netap_profile_release_resource();
1006}
1007
1008module_init(netap_profile_init_module);
1009module_exit(netap_profile_exit_module);
1010
1011MODULE_LICENSE("Dual BSD/GPL");