blob: 50be1fc7f1ddca5b823c78220a967650dfffafac [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>
Sundarajan Srinivasand09d7dd2014-12-10 16:24:21 -080037#include <nss_api_if.h>
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -080038
39#include "profilenode.h"
40#include "profpkt.h"
41
42/*
43 * This is the driver for the NetAP Core profiler. The system interface to the driver is
44 * profile_register_performance_counter(), defined in <asm/profile.>
45 * a set of proc files (proc/profile/<*>), used by the profiler daemon
46 *
47 * communication between the profiler components is described in a set of header files.
48 * There are multiple versions of these files that must be kept synchronized:
49 * in nss/source/pkg/profile
50 * in tools/profiler
51 * in qsdk/qca/src/qca-nss-drv/profiler
52 *
53 * profilesample.h specifies the sample format used by pkg/profile, profile driver, and ip3kprof (two versions)
54 * profilenode.h specifies the driver node communication between NetAP and the profile driver. (two versions)
55 * profpkt.h specifies the network packet format between the profile driver, profile daemon, and ip3kprof (two versions)
56 *
57 *
58 * NSS profile sampler:
59 * pkg/profile/src/profile.c
60 * pkg/profile/include/profilenode.h
61 * pkg/profile/include/profilesample.h
62 *
63 * profile driver: this code
64 * qsdk/qca/src/qca-nss-drv/profiler
65 *
66 * profilerd: the user daemon that sends data to the tool
67 * qsdk/qca/feeds/qca/utils/profilerd
68 *
69 * ubicom32-prof: the Windows tool
70 * tools/profiler/src/(many files)
71 *
72 */
73
74#ifdef PROFILE_DEBUG
75#define profileDebug(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
76#define profileInfo(s, ...) pr_info("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
77#define profileWarn(s, ...) pr_warn("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
78#else
79#define profileDebug(s, ...)
80#define profileInfo(s, ...)
81#define profileWarn(s, ...)
82#endif
83
84static void profiler_handle_reply(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm);
85
86/*
87 * LINUX and Ultra counters must all fit in one packet
88 */
89#define PROFILE_LINUX_MAX_COUNTERS 40
90static int profile_num_counters = 0;
91static volatile unsigned int *profile_counter[PROFILE_LINUX_MAX_COUNTERS];
92static char profile_name[PROFILE_LINUX_MAX_COUNTERS][PROFILE_COUNTER_NAME_LENGTH];
93
94/*
95 * internal function to check if @name has been registered before
96 * return the found index, or -1 otherwise
97 */
98static int __profile_find_entry(char *name)
99{
100 int i;
101
102 for (i = 0; i < profile_num_counters; i++) {
103 if (!strncasecmp(name, profile_name[i], PROFILE_COUNTER_NAME_LENGTH)) {
104 return i;
105 }
106 }
107 return -1;
108}
109
110/*
111 * profile_register_performance_counter - register @counter into profile tracking list by key @name
112 * @counter: pointer of the counter variable
113 * @name: identifier of this counter
114 *
115 * Returns zero if total entries exceeding PROFILE_LINUX_MAX_COUNTERS
116 * non-zero otherwise.
117 *
118 * Each @name gives unique entry for @counter, by allocating a new array slot or just use existing one.
119 * No need of de-registration API, since a loadable module's new insmod, will replace the
120 * @counter's * new address at the same profile_counter[] slot.
121 */
122int profile_register_performance_counter(volatile unsigned int *counter, char *name)
123{
124 int i;
125
126 if (profile_num_counters >= PROFILE_LINUX_MAX_COUNTERS) {
127 return 0;
128 }
129
130 i = __profile_find_entry(name);
131 if (i < 0) {
132 i = profile_num_counters++;
133 }
134
135 profile_counter[i] = counter;
Guojun Jind3328392016-01-22 14:14:17 -0800136 strlcpy(profile_name[i], name, PROFILE_COUNTER_NAME_LENGTH);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800137 profile_name[i][PROFILE_COUNTER_NAME_LENGTH - 1] = 0;
138
139 return 1;
140}
141
142/*
143 * make a packet full of sample data
144 */
145static int profile_make_data_packet(char *buf, int blen, struct profile_io *pn)
146{
147 int sp_samples = 0; /* separated samples if any */
148 int ns; /* number of samples requested */
149 struct profile_header ph;
150 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
151
152 if (blen < sizeof(ph) + sizeof(struct profile_sample)) {
153 return -EINVAL;
154 }
155
156 profileDebug("%p stat %x cnt %d %p\n", pn->pnc.pn2h, pn->pnc.pn2h->mh.md_type, psc_hd->count, pn->ccl);
157
158 if (pn->pnc.pn2h->mh.md_type == PINGPONG_EMPTY || psc_hd->count < 1) {
159 struct profile_n2h_sample_buf *nsb;
160 ns = (pn->ccl_read + 1) & (CCL_SIZE-1);
161 nsb = pn->ccl + ns;
162 if (ns == pn->ccl_write || nsb->mh.md_type != PINGPONG_FULL) {
163 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);
164 return -EAGAIN;
165 }
166 pn->ccl_read = ns;
167 profileInfo("sp %p => %p rd %d %p\n", pn->pnc.samples, nsb->samples, ns, nsb);
168 psc_hd = &nsb->psc_header;
169 pn->pnc.pn2h = nsb;
170 pn->pnc.samples = nsb->samples;
171 pn->pnc.cur = 0;
172 }
173 pn->pnc.pn2h->mh.md_type = PINGPONG_INUSE;
174
175 /*
176 * fill in the packet header
177 */
178 memset(&ph, 0, sizeof(ph));
179 ph.pph.magic = htons(PROF_MAGIC + PROFILE_VERSION);
180 ph.pph.header_size = sizeof(ph);
181 ph.pph.profile_instructions = 0;
182 ph.pph.clock_freq = pn->pnc.un.cpu_freq;
183 ph.pph.ddr_freq = pn->pnc.un.ddr_freq;
184 ph.pph.cpu_id = pn->pnc.un.cpu_id;
185 ph.pph.seq_num = htonl(pn->profile_sequence_num);
186 ph.pph.sample_stack_words = htonl(PROFILE_STACK_WORDS);
187
188 ns = (blen - sizeof(ph)) / sizeof(struct profile_sample);
189 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));
190 if (ns > psc_hd->count)
191 ns = psc_hd->count;
192 if (ns == 0) {
193 printk("NS should not be 0: rlen %d hd cnt %d\n", blen, psc_hd->count);
194 return 0;
195 }
196
197 /*
198 * if buf cannot hold all samples, then samples must be separated by set.
199 */
200 if (ns < psc_hd->count) {
201 ph.exh.sets_map = psc_hd->exh.sets_map; /* save for separating sets */
202 do {
203 sp_samples += psc_hd->exh.sets_map & 0x0F;
204 psc_hd->exh.sets_map >>= 4; /* remove the last set */
205 psc_hd->exh.sample_sets--;
206 ph.exh.sample_sets++; /* save for restore later */
207 } while ((psc_hd->count - sp_samples) > ns);
208 ns = psc_hd->count - sp_samples;
209 }
210 ph.pph.sample_count = ns;
211 if (copy_to_user(buf, &ph.pph, sizeof(ph.pph)) != 0) {
212 return -EFAULT;
213 }
214 buf += sizeof(ph.pph);
215
216 /*
217 * ph.exh is unused dummy; and psc_hd->exh is used directly to avoid double mem copy
218 */
219 if (copy_to_user(buf, &psc_hd->exh, sizeof(psc_hd->exh)) != 0) {
220 return -EFAULT;
221 }
222 buf += sizeof(psc_hd->exh);
223
224 blen = ns * sizeof(struct profile_sample);
225 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));
226 if (copy_to_user(buf, &pn->pnc.samples[pn->pnc.cur], blen) != 0) {
227 return -EFAULT;
228 }
229 pn->pnc.cur += ns;
230 psc_hd->count -= ns;
231 if (psc_hd->count < 1)
232 pn->pnc.pn2h->mh.md_type = PINGPONG_EMPTY;
233
234 /*
235 * restore left over sample counts; 0s for no one
236 */
237 if (sp_samples) {
238 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);
239 psc_hd->exh.sample_sets = ph.exh.sample_sets;
240 psc_hd->exh.sets_map = ph.exh.sets_map;
241 }
242
243 pn->profile_sequence_num++;
244 blen += sizeof(ph);
245 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);
246 return blen;
247}
248
249/*
250 * This is no longer needed due to NetAP and Linux use different CPUs, and profile is NetAP only.
251 * All related code will be removed after corresponging code in visual tool is corrected; otherwise
252 * visual tool will mis-behave
253 */
254struct profile_counter profile_builtin_stats[] =
255{
256 {
257 "Free memory(KB)", 0
258 },
259 {
260 "Max free Block(KB)", 0
261 }
262};
263
264/*
265 * make a packet full of performance counters
266 */
267static int profile_make_stats_packet(char *buf, int bytes, struct profile_io *pn)
268{
269 static char prof_pkt[PROFILE_MAX_PACKET_SIZE];
270
271 char *ptr;
272 int n;
273 struct profile_counter *counter_ptr;
274 struct profile_header_counters *hdr = (struct profile_header_counters *)prof_pkt;
275 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
276
277 if (bytes > PROFILE_MAX_PACKET_SIZE) {
278 bytes = PROFILE_MAX_PACKET_SIZE;
279 }
280 n = sizeof(profile_builtin_stats) + (pn->pnc.un.num_counters + profile_num_counters) * sizeof(*counter_ptr);
281
282 if ((bytes - sizeof(hdr)) < n) {
283 profileWarn("room too small %d for cnts %d\n", bytes, n);
284 return 0;
285 }
286
287 hdr->magic = htons(PROF_MAGIC_COUNTERS);
288 hdr->ultra_count = htons(pn->pnc.un.num_counters);
289 hdr->linux_count = htonl(profile_num_counters + sizeof(profile_builtin_stats) / sizeof(*counter_ptr));
290 hdr->ultra_sample_time = psc_hd->exh.clocks;
291 hdr->linux_sample_time = psc_hd->exh.clocks; /* QSDK has no time func */
292
293 n = pn->pnc.un.num_counters; /* copy NSS counters */
294 n *= sizeof(pn->pnc.un.counters[0]);
295 ptr = (char*) (hdr + 1);
296 memcpy(ptr, (void *)(pn->pnc.un.counters), n);
297 ptr += n;
298
299 counter_ptr = (struct profile_counter *)ptr;
300 for (n = 0; n < profile_num_counters; ++n) {
301 counter_ptr->value = htonl(*profile_counter[n]);
Guojun Jind3328392016-01-22 14:14:17 -0800302 strlcpy(counter_ptr->name, profile_name[n],
303 PROFILE_COUNTER_NAME_LENGTH);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800304 counter_ptr++;
305 }
306 ptr = (char*)counter_ptr;
307
308 /*
309 * built in statistics
310 profile_get_memory_stats(&total_free, &max_free);
311 */
312 profile_builtin_stats[0].value = 0;
313 profile_builtin_stats[1].value = 0;
314 memcpy(ptr, (void *)profile_builtin_stats, sizeof(profile_builtin_stats));
315 ptr += sizeof(profile_builtin_stats);
316
317 n = ptr - prof_pkt;
318 if (copy_to_user(buf, prof_pkt, n) != 0) {
319 return -EFAULT;
320 }
321 return n;
322}
323
324/*
325 * space for all memory blocks so we can hold locks for short time when walking tables
326 */
327static struct profile_io *node[NSS_MAX_CORES];
328
329static int profile_open(struct inode *inode, struct file *filp)
330{
331 int n;
332 struct profile_io *pn;
333
334 if (filp->private_data)
Shashank Balashankar0b352e62016-03-29 15:48:36 -0700335 printk(KERN_WARNING "%s: %p\n", filp->f_path.dentry->d_iname, filp->private_data);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800336
Shashank Balashankar0b352e62016-03-29 15:48:36 -0700337 n = filp->f_path.dentry->d_iname[strlen(filp->f_path.dentry->d_iname) - 1] - '0';
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800338 if (n < 0 || n >= NSS_MAX_CORES)
339 n = 0;
340 pn = node[n];
341 if (!pn) {
342 return -ENOENT;
343 }
344
345 if (!pn->pnc.enabled && nss_get_state(pn->ctx) == NSS_STATE_INITIALIZED) {
346 nss_tx_status_t ret;
347 pn->pnc.enabled = 1;
348 pn->profile_first_packet = 1;
349 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_START_MSG;
350 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un, sizeof(pn->pnc.un), profiler_handle_reply);
351 profileInfo("%s: %d -- %p: ccl %p sp %p\n", __func__, ret, pn, pn->ccl, pn->pnc.samples);
352 filp->private_data = pn;
353 return 0;
354 }
355
356 profileWarn("profile ena %d nss stat %x\n", pn->pnc.enabled, nss_get_state(pn->ctx));
357 return -EBUSY;
358}
359
360/*
361 * return a udp packet ready to send to the profiler tool
362 * when there are no packets left to make, return 0
363 */
364static int profile_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
365{
366 int result = 0;
367 int slen = 0;
368 struct profile_io *pn = (struct profile_io *)filp->private_data;
369 if (!pn) {
370 return -ENOENT;
371 }
372
373 if (!pn->pnc.enabled) {
374 return -EPERM;
375 }
376 if (!pn->pnc.samples) {
377 return -ENOMEM;
378 }
379
380 if (pn->profile_first_packet) {
381 result = profile_make_stats_packet(buf, count, pn);
382 pn->profile_first_packet = 0;
383 profileInfo("%d profile_make_stats_packet %d\n", result, count);
384
385#ifdef PROFILE_SEP_STAT
386 /*
387 * currectly, stat and sample data are combined in one pkt for efficient;
388 * but this is harder to debug and required remote tool to handle
389 * packet in all-in-one method instead of individual handler.
390 */
391 return result;
392#endif
393 }
394
395 if (result > 0) {
396 buf += result;
397 count -= result;
398 slen = result;
399 }
400 result = profile_make_data_packet(buf, count, pn);
401 if (result == 0) {
402 pn->profile_first_packet = 1;
403 }
404 profileInfo("%d: profile_make_data_packet %d %d\n", result, count, slen);
405
406 profileInfo("%d: read\n", pn->pnc.enabled);
407 if (pn->pnc.enabled < 0) {
408 nss_tx_status_t ret;
409 pn->pnc.enabled = 1;
410 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_START_MSG;
411 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un, sizeof(pn->pnc.un), profiler_handle_reply);
412 profileWarn("%s: restart %d -- %p: ccl %p sp %p\n", __func__, ret, pn, pn->ccl, pn->pnc.samples);
413 }
414
415 return result + slen;
416}
417
418/*
419 * the close function paired with profiler_open
420 */
421static int profile_release(struct inode *inode, struct file *filp)
422{
423 struct profile_io *pn = (struct profile_io *)filp->private_data;
424 if (!pn) {
425 return -ENOENT;
426 }
427
428 if (pn->pnc.enabled) {
429 nss_tx_status_t ret;
430 pn->pnc.enabled = 0;
431 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_STOP_MSG;
432 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un, sizeof(pn->pnc.un), profiler_handle_reply);
433 profileInfo("%s: %p %d\n", __func__, pn, ret);
434 return 0;
435 }
436 profileWarn("%s: attempt closing non-open dev %p\n", __func__, pn);
437 pn->profile_first_packet = 1;
438 return -EBADF;
439}
440
441#define isspace(c) (c==' ' || c=='\t')
442
443static int parseDbgData(const char *buf, size_t count, struct debug_box *db)
444{
445 char *cp;
446 int n;
447
448 printk("%p: buf (%s) cnt %d\n", buf, buf, count);
449 if (sscanf(buf, "%x", (uint32_t *)&db->base_addr) != 1) {
450 printk("%s: cannot get base addr\n", __func__);
451 return -EINVAL;
452 }
453
454 cp = strchr(buf, ' ');
455 if (!cp) {
456noea: printk("%s: no enough arguments\n", __func__);
457 return -EFAULT;
458 }
459
460 while (isspace(*cp)) cp++;
461 if (!strncmp(cp, "mio", 3) || !strncmp(cp, "moveio", 6)) {
462 printk("%p: cp (%s)\n", cp, cp);
463 cp = strchr(cp, ' ');
464 if (!cp) {
465 goto noea;
466 }
467 db->opts |= DEBUG_OPT_MOVEIO;
468 }
469
470 while (isspace(*cp)) cp++;
471 printk("base addr %p -- %s\n", db->base_addr, cp);
472
473 if (!strncmp(cp, "read", 4)) {
474 cp = strchr(cp, ' ');
475 if (cp) {
476 while (isspace(*cp)) cp++;
477 sscanf(cp, "%x", &db->dlen);
478 }
479 return 0;
480 }
481
482 n = 0;
483 do {
484 while (isspace(*cp)) cp++;
485 if (sscanf(cp, "%x", db->data+n) != 1) {
486 printk("n %d : %s\n", n, cp);
487 break;
488 }
489 printk("write %x to off %x\n", db->data[n], n * sizeof(db->data[0]));
490 n++;
491 cp = strchr(cp, ' ');
492 } while (cp && n < MAX_DB_WR);
493 return n;
494}
495
496/*
497 * display memory content read from Phy addr
498 */
499static void debug_if_show(struct debug_box *db, int buf_len)
500{
501 int i;
502
503 for (i=0; i < db->dlen; i++) {
504 if ((i & 3) == 0)
505 printk("\n%p: ", db->base_addr + i);
506 printk("%9x", db->data[i]);
507 }
508 printk("\ndumped %d (extra 1) blen %d\n", db->dlen, buf_len);
509}
510
511/*
512 * show debug message we requested from NSS
513 */
514static void profiler_handle_debug_reply(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm)
515{
516 debug_if_show((struct debug_box*)&ncm[1], ncm->len);
517}
518
519/*
520 * a generic Krait <--> NSS debug interface
521 */
522static int debug_if(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
523{
524 int result;
525 struct debug_box *db;
526 struct profile_io *pio = node[0];
527
528 if (!pio) {
529 return -ENOENT;
530 }
531
532 if (!pio->pnc.enabled) {
533 return -EPERM;
534 }
535
536 db = (struct debug_box *) &pio->pnc;
537 db->dlen = db->opts = 0;
538 result = parseDbgData(buf, count, db);
539 if (result < 0) {
540 return result;
541 }
542
543 if (!result) {
544 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_DEBUG_RD_MSG;
545 } else {
546 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_DEBUG_WR_MSG;
547 db->dlen = result;
548 }
549 result = nss_profiler_if_tx_buf(pio->ctx, &pio->pnc.un, sizeof(pio->pnc.un), profiler_handle_debug_reply);
550 printk("dbg res %d dlen = %d opt %x\n", result, db->dlen, db->opts);
551 return count;
552}
553
554static const struct file_operations profile_fops = {
555 .open = profile_open,
556 .read = profile_read,
557 .release = profile_release,
558 .write = debug_if,
559};
560
561/*
562 * showing sample status on Linux console
563 */
564static int profile_rate_show(struct seq_file *m, void *v)
565{
566 struct profile_io *pn = node[0];
567 if (pn) {
568 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
569 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",
570 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);
571 } else {
572 seq_printf(m, "Profiler is not initialized.\n");
573 }
574 return 0;
575}
576
577static int profile_rate_open(struct inode *inode, struct file *filp)
578{
579 return single_open(filp, profile_rate_show, NULL);
580}
581
582static int profile_rate_write(struct file *filp, const char *buf, size_t len, loff_t *off)
583{
584 *off = 0;
585 return 0;
586}
587
588static const struct file_operations profile_rate_fops = {
589 .open = profile_rate_open,
590 .read = seq_read,
591 .llseek = seq_lseek,
592 .release = single_release,
593 .write = profile_rate_write,
594};
595
596/*
597 * hex dump for debug
598 */
599static void kxdump(void *buf, int len, const char *who)
600{
601 int *ip = (int*) buf;
602 int lns = len >> 5; /* 32-B each line */
603 if (lns > 4)
604 lns = 4;
605 printk("%p: kxdump %s: len %d\n", buf, who, len);
606 do {
607 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]);
608 ip += 8;
609 } while (lns--);
610}
611
612/*
613 * check magic # and detect Endian.
614 * negtive return means failure.
615 * return 1 means need to ntoh swap.
616 */
617static int profiler_magic_verify(struct profile_sample_ctrl_header *psc_hd, int buf_len)
618{
619 int swap = 0;
620 if ((psc_hd->hd_magic & UBI32_PROFILE_HD_MMASK) != UBI32_PROFILE_HD_MAGIC) {
621 if ((psc_hd->hd_magic & UBI32_PROFILE_HD_MMASK_REV) != UBI32_PROFILE_HD_MAGIC_REV) {
622 kxdump(psc_hd, buf_len, "bad profile packet");
623 printk("bad profile packet %x : %d\n", psc_hd->hd_magic, buf_len);
624 return -1;
625 }
626 profileDebug("Profile data in different Endian type %x\n", psc_hd->hd_magic);
627 swap = 1;
628 psc_hd->hd_magic = ntohl(psc_hd->hd_magic);
629 }
630 return swap;
631}
632
633/*
634 * process profile sample data from NSS
635 */
636static void profile_handle_nss_data(void *arg, struct nss_profiler_msg *npm)
637{
638 int buf_len = npm->cm.len;
639 void *buf = &npm->payload;
640 struct profile_io *pn;
641 struct profile_n2h_sample_buf *nsb;
642 struct profile_sample_ctrl_header *psc_hd = (struct profile_sample_ctrl_header *)buf;
643 int ret, wr;
644 int swap = 0; /* only for header and info data, not samples */
645
646 if (buf_len < (sizeof(struct profile_session) - sizeof(struct profile_counter) * (PROFILE_MAX_APP_COUNTERS))) {
647 printk("profile data packet is too small to be useful %d\n", buf_len);
648 return;
649 }
650
651 swap = profiler_magic_verify(psc_hd, buf_len);
652 if (swap < 0) {
653 return;
654 }
655
656 pn = (struct profile_io *)arg;
657 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));
658 //kxdump(buf, buf_len, "process profile packet");
659
660 if (npm->cm.type == NSS_PROFILER_FIXED_INFO_MSG) {
661 struct profile_session *pTx = (struct profile_session *)buf;
662 if (swap) {
663 pn->pnc.un.rate = ntohl(pTx->rate);
664 pn->pnc.un.cpu_id = ntohl(pTx->cpu_id);
665 pn->pnc.un.cpu_freq = ntohl(pTx->cpu_freq);
666 pn->pnc.un.ddr_freq = ntohl(pTx->ddr_freq);
667 pn->pnc.un.num_counters = ntohl(pTx->num_counters);
668 } else {
669 pn->pnc.un = *pTx;
670 }
671 memcpy(pn->pnc.un.counters, pTx->counters, pn->pnc.un.num_counters * sizeof(pn->pnc.un.counters[0]));
672 pn->profile_first_packet = 1;
673 return;
674 }
675
676 wr = (pn->ccl_write + 1) & (CCL_SIZE-1);
677 nsb = pn->ccl + wr;
678 swap = (pn->ccl_read - wr) & (CCL_SIZE-1); /* PROFILER_FLOWCTRL */
679 if (nsb->mh.md_type != PINGPONG_EMPTY || (swap && swap < 5)) {
680 if (pn->pnc.enabled > 0) {
681 pn->pnc.enabled = -1;
682 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_STOP_MSG;
683 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un, sizeof(pn->pnc.un), profiler_handle_reply);
684 profileWarn("%d temp stop sampling engine %d\n", swap, ret);
685 }
686 if (swap < 3) {
687 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);
688 return; /* -EMSGSIZE */
689 }
690 }
691 pn->ccl_write = wr;
692
693 /*
694 * sampling data -- hdr NBO swap is done at NSS side via SWAPB.
695 */
696 memcpy(&nsb->psc_header, buf, buf_len); /* pn->pnc.pn2h->psc_header = *psc_hd; maybe faster, but take more memory */
697
698 nsb->mh.md_type = PINGPONG_FULL;
699 //kxdump((void*)(nsb->samples + 23), sizeof(*nsb->samples) << 1, "1st 2 samples");
700 if (!wr) {
701 /*
702 * should be UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_COUNTERS_MSG
703 * but FW is hard to change due to packge warehouse, so using
704 * STOP/START instead till PROFILER_COUNTERS_MSG done in FW
705 */
706 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_STOP_MSG;
707 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un, sizeof(pn->pnc.un), profiler_handle_reply);
708 if (ret == NSS_TX_FAILURE)
709 printk("STOP Cmd failed %d %d\n", ret, wr);
710 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_START_MSG;
711 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un, sizeof(pn->pnc.un), profiler_handle_reply);
712 }
713 profileInfo("filled %p %p wr %d\n", nsb, nsb->samples, pn->ccl_write);
714}
715
716/*
717 * process N2H reply for message we sent to NSS -- currently no action
718 */
719static void profiler_handle_reply(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm)
720{
721 switch (ncm->response) {
722 default:
Sundarajan Srinivasand09d7dd2014-12-10 16:24:21 -0800723 profileWarn("%p: profiler had error response %d\n", nss_ctx, ncm->response);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800724 /*
725 * fail through -- no plan to do anything yet
726 */
727 case NSS_CMN_RESPONSE_ACK:
728 return;
729 }
730}
731
732/*
733 * initialize basic profile data structure
734 */
735static void profile_init(struct profile_io *node)
736{
737 int n;
738
739 memset(&node->pnc, 0, sizeof(node->pnc));
740 node->ccl_read = 0;
741 node->ccl_write = -1;
742 node->pnc.pn2h = node->ccl;
743 node->pnc.samples = node->ccl->samples;
744
745 for (n = 0; n < CCL_SIZE; n++) {
746 node->ccl[n].mh.md_type = PINGPONG_EMPTY;
747 node->ccl[n].psc_header.count = 0;
748 }
749
750 /*
751 * sw_ksp is an array of pointers to struct thread_info, the current task executing for each linux virtual processor
752 node->sw_ksp_ptr = sw_ksp;
753 */
754 node->task_offset = offsetof(struct thread_info, task);
755 node->pid_offset = offsetof(struct task_struct, tgid);
756}
757
758static struct proc_dir_entry *pdir;
759
760/*
761 * init_module cannot call exit_MODULE, so use this wrapper
762 */
763void netap_profile_release_resource(void)
764{
765 if (pdir) {
766 remove_proc_entry("rate", pdir);
767 remove_proc_entry("data", pdir);
768 remove_proc_entry("data1", pdir);
769 }
770 kfree(node[0]->ccl);
771 kfree(node[0]);
772 node[0] = NULL;
773}
774
775/*
776 * kernel module entry
777 */
778int __init netap_profile_init_module(void)
779{
Murat Sezgin3441e772015-10-26 11:55:57 -0700780#ifdef CONFIG_OF
781 /*
782 * If the node is not compatible, don't do anything.
783 */
784 if (!of_find_node_by_name(NULL, "nss-common")) {
785 return 0;
786 }
787#endif
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800788 /*
789 * we need N nodes, not one node + N ctx, for N cores
790 */
791 node[0] = kmalloc(sizeof(*node[0]) * NSS_MAX_CORES, GFP_KERNEL);
792 if (!node[0]) {
793 printk(KERN_INFO "Profiler CTRL kmalloc failed.\n");
794 return -ENOMEM;
795 }
796
797 node[0]->ccl = kmalloc(sizeof(*node[0]->ccl) * CCL_SIZE * NSS_MAX_CORES, GFP_KERNEL);
798 if (!node[0]->ccl) {
799 printk(KERN_INFO "Profiler n2h_sample_buf kmalloc failed.\n");
800 kfree(node[0]);
801 node[0] = NULL;
802 return -ENOMEM;
803 }
804
805 /*
806 * connect to the file system
807 */
808 pdir = proc_mkdir("profile", NULL);
809 if (!pdir ||
810 !proc_create("data", 0, pdir, &profile_fops) ||
811 !proc_create("data1", 0, pdir, &profile_fops) ||
812 !proc_create("rate", 0, pdir, &profile_rate_fops)) {
813 netap_profile_release_resource();
814 return -ENOMEM;
815 }
816
817 profile_init(node[0]);
818
819 /*
820 * attatch the device callback to N2H channel for CPU 0
821 */
822 node[0]->ctx = nss_profiler_notify_register(NSS_CORE_0, profile_handle_nss_data, node[0]);
823#if NSS_MAX_CORES > 1
824 node[1] = node[0] + 1;
825 node[1]->ccl = node[0]->ccl + CCL_SIZE;
826
827 profile_init(node[1]);
828 node[1]->ctx = nss_profiler_notify_register(NSS_CORE_1, profile_handle_nss_data, node[1]);
829 profile_register_performance_counter(&node[1]->profile_sequence_num, "Profile1 DRV data packets");
830#endif
831
832 profile_register_performance_counter(&node[0]->profile_sequence_num, "Profile0 DRV data packets");
833 return 0;
834}
835
836/*
837 * kernel module exit
838 */
839void __exit netap_profile_exit_module(void)
840{
Murat Sezgin3441e772015-10-26 11:55:57 -0700841#ifdef CONFIG_OF
842 /*
843 * If the node is not compatible, don't do anything.
844 */
845 if (!of_find_node_by_name(NULL, "nss-common")) {
846 return;
847 }
848#endif
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800849 nss_profiler_notify_unregister(NSS_CORE_0);
850#if NSS_MAX_CORES > 1
851 nss_profiler_notify_unregister(NSS_CORE_1);
852#endif
853 netap_profile_release_resource();
854}
855
856module_init(netap_profile_init_module);
857module_exit(netap_profile_exit_module);
858
859MODULE_LICENSE("Dual BSD/GPL");