blob: 305f9dfbaa66b437dd25cca0fa45e078318bc224 [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 Jine4fec2f2017-05-16 17:14:23 -0700659static ssize_t debug_if(struct file *filp,
660 const char __user *ubuf, size_t count, loff_t *f_pos)
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800661{
Guojun Jine4fec2f2017-05-16 17:14:23 -0700662 char *buf;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800663 int result;
664 struct debug_box *db;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700665 struct profile_io *pio = (struct profile_io *)filp->private_data;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800666
667 if (!pio) {
668 return -ENOENT;
669 }
670
671 if (!pio->pnc.enabled) {
672 return -EPERM;
673 }
674
Guojun Jine4fec2f2017-05-16 17:14:23 -0700675 buf = kmalloc(count, GFP_KERNEL);
676 if (!buf)
677 return -ENOMEM;
678
679 if (copy_from_user(buf, ubuf, count)) {
680 kfree(buf);
681 printk(KERN_ERR "copy_from_user\n");
682 return -EIO;
683 }
684
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800685 db = (struct debug_box *) &pio->pnc;
686 db->dlen = db->opts = 0;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700687
688 if (!isdigit(buf[0])) {
689 result = parse_sys_stat_event_req(buf, count, db, pio);
Guojun Jine4fec2f2017-05-16 17:14:23 -0700690 kfree(buf);
Guojun Jin3deae8c2016-08-23 15:51:21 -0700691
692 if ((result > 0) && (filp->f_flags & O_RDWR)) {
693 /*
694 * set flag so event-counter can read the data from FW
695 */
696 pio->sw_ksp_ptr = (uint32_t *)db;
697 }
698 return result;
699 }
700
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800701 result = parseDbgData(buf, count, db);
Guojun Jine4fec2f2017-05-16 17:14:23 -0700702 kfree(buf);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800703 if (result < 0) {
704 return result;
705 }
706
707 if (!result) {
708 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_DEBUG_RD_MSG;
709 } else {
710 db->hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_DEBUG_WR_MSG;
711 db->dlen = result;
712 }
Guojun Jin3deae8c2016-08-23 15:51:21 -0700713 result = nss_profiler_if_tx_buf(pio->ctx, &pio->pnc.un,
714 sizeof(pio->pnc.un), profiler_handle_debug_reply, pio);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800715 printk("dbg res %d dlen = %d opt %x\n", result, db->dlen, db->opts);
716 return count;
717}
718
719static const struct file_operations profile_fops = {
720 .open = profile_open,
721 .read = profile_read,
722 .release = profile_release,
723 .write = debug_if,
724};
725
726/*
727 * showing sample status on Linux console
728 */
729static int profile_rate_show(struct seq_file *m, void *v)
730{
731 struct profile_io *pn = node[0];
732 if (pn) {
733 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
734 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",
735 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);
736 } else {
737 seq_printf(m, "Profiler is not initialized.\n");
738 }
739 return 0;
740}
741
742static int profile_rate_open(struct inode *inode, struct file *filp)
743{
744 return single_open(filp, profile_rate_show, NULL);
745}
746
Guojun Jin76cf1392017-05-02 12:02:31 -0700747static ssize_t profile_rate_write(struct file *filp, const char *buf, size_t len, loff_t *off)
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800748{
749 *off = 0;
750 return 0;
751}
752
753static const struct file_operations profile_rate_fops = {
754 .open = profile_rate_open,
755 .read = seq_read,
756 .llseek = seq_lseek,
757 .release = single_release,
758 .write = profile_rate_write,
759};
760
761/*
762 * hex dump for debug
763 */
764static void kxdump(void *buf, int len, const char *who)
765{
766 int *ip = (int*) buf;
767 int lns = len >> 5; /* 32-B each line */
768 if (lns > 4)
769 lns = 4;
770 printk("%p: kxdump %s: len %d\n", buf, who, len);
771 do {
772 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]);
773 ip += 8;
774 } while (lns--);
775}
776
777/*
778 * check magic # and detect Endian.
779 * negtive return means failure.
780 * return 1 means need to ntoh swap.
781 */
782static int profiler_magic_verify(struct profile_sample_ctrl_header *psc_hd, int buf_len)
783{
784 int swap = 0;
785 if ((psc_hd->hd_magic & UBI32_PROFILE_HD_MMASK) != UBI32_PROFILE_HD_MAGIC) {
786 if ((psc_hd->hd_magic & UBI32_PROFILE_HD_MMASK_REV) != UBI32_PROFILE_HD_MAGIC_REV) {
787 kxdump(psc_hd, buf_len, "bad profile packet");
788 printk("bad profile packet %x : %d\n", psc_hd->hd_magic, buf_len);
789 return -1;
790 }
791 profileDebug("Profile data in different Endian type %x\n", psc_hd->hd_magic);
792 swap = 1;
793 psc_hd->hd_magic = ntohl(psc_hd->hd_magic);
794 }
795 return swap;
796}
797
798/*
799 * process profile sample data from NSS
800 */
801static void profile_handle_nss_data(void *arg, struct nss_profiler_msg *npm)
802{
803 int buf_len = npm->cm.len;
804 void *buf = &npm->payload;
805 struct profile_io *pn;
806 struct profile_n2h_sample_buf *nsb;
807 struct profile_sample_ctrl_header *psc_hd = (struct profile_sample_ctrl_header *)buf;
808 int ret, wr;
809 int swap = 0; /* only for header and info data, not samples */
810
811 if (buf_len < (sizeof(struct profile_session) - sizeof(struct profile_counter) * (PROFILE_MAX_APP_COUNTERS))) {
812 printk("profile data packet is too small to be useful %d\n", buf_len);
813 return;
814 }
815
816 swap = profiler_magic_verify(psc_hd, buf_len);
817 if (swap < 0) {
818 return;
819 }
820
821 pn = (struct profile_io *)arg;
822 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));
823 //kxdump(buf, buf_len, "process profile packet");
824
825 if (npm->cm.type == NSS_PROFILER_FIXED_INFO_MSG) {
826 struct profile_session *pTx = (struct profile_session *)buf;
827 if (swap) {
828 pn->pnc.un.rate = ntohl(pTx->rate);
829 pn->pnc.un.cpu_id = ntohl(pTx->cpu_id);
830 pn->pnc.un.cpu_freq = ntohl(pTx->cpu_freq);
831 pn->pnc.un.ddr_freq = ntohl(pTx->ddr_freq);
Guojun Jin3deae8c2016-08-23 15:51:21 -0700832 pn->pnc.un.num_counters = pTx->num_counters;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800833 } else {
834 pn->pnc.un = *pTx;
835 }
836 memcpy(pn->pnc.un.counters, pTx->counters, pn->pnc.un.num_counters * sizeof(pn->pnc.un.counters[0]));
837 pn->profile_first_packet = 1;
838 return;
839 }
840
841 wr = (pn->ccl_write + 1) & (CCL_SIZE-1);
842 nsb = pn->ccl + wr;
843 swap = (pn->ccl_read - wr) & (CCL_SIZE-1); /* PROFILER_FLOWCTRL */
844 if (nsb->mh.md_type != PINGPONG_EMPTY || (swap && swap < 5)) {
845 if (pn->pnc.enabled > 0) {
846 pn->pnc.enabled = -1;
847 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_STOP_MSG;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700848 ret = nss_profiler_if_tx_buf(pn->ctx,
849 &pn->pnc.un, sizeof(pn->pnc.un),
850 profiler_handle_reply, pn);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800851 profileWarn("%d temp stop sampling engine %d\n", swap, ret);
852 }
853 if (swap < 3) {
854 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);
855 return; /* -EMSGSIZE */
856 }
857 }
858 pn->ccl_write = wr;
859
860 /*
861 * sampling data -- hdr NBO swap is done at NSS side via SWAPB.
862 */
863 memcpy(&nsb->psc_header, buf, buf_len); /* pn->pnc.pn2h->psc_header = *psc_hd; maybe faster, but take more memory */
864
865 nsb->mh.md_type = PINGPONG_FULL;
Guojun Jin3deae8c2016-08-23 15:51:21 -0700866
867 /*
868 * ask for perf_counters (software counters) update every 32 samples
869 */
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800870 if (!wr) {
Guojun Jin3deae8c2016-08-23 15:51:21 -0700871 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | NSS_PROFILER_COUNTERS_MSG;
872 ret = nss_profiler_if_tx_buf(pn->ctx, &pn->pnc.un,
873 sizeof(pn->pnc.un), profiler_handle_reply, pn);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800874 if (ret == NSS_TX_FAILURE)
Guojun Jin3deae8c2016-08-23 15:51:21 -0700875 printk("req counters Cmd failed %d %d\n", ret, wr);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800876 }
877 profileInfo("filled %p %p wr %d\n", nsb, nsb->samples, pn->ccl_write);
878}
879
880/*
881 * process N2H reply for message we sent to NSS -- currently no action
882 */
883static void profiler_handle_reply(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm)
884{
885 switch (ncm->response) {
886 default:
Sundarajan Srinivasand09d7dd2014-12-10 16:24:21 -0800887 profileWarn("%p: profiler had error response %d\n", nss_ctx, ncm->response);
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800888 /*
889 * fail through -- no plan to do anything yet
890 */
891 case NSS_CMN_RESPONSE_ACK:
892 return;
893 }
894}
895
896/*
897 * initialize basic profile data structure
898 */
899static void profile_init(struct profile_io *node)
900{
901 int n;
902
903 memset(&node->pnc, 0, sizeof(node->pnc));
904 node->ccl_read = 0;
905 node->ccl_write = -1;
906 node->pnc.pn2h = node->ccl;
907 node->pnc.samples = node->ccl->samples;
908
909 for (n = 0; n < CCL_SIZE; n++) {
910 node->ccl[n].mh.md_type = PINGPONG_EMPTY;
911 node->ccl[n].psc_header.count = 0;
912 }
913
914 /*
Guojun Jin3deae8c2016-08-23 15:51:21 -0700915 * sw_ksp is an array of pointers to struct thread_info,
916 * the current task executing for each linux virtual processor
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800917 node->sw_ksp_ptr = sw_ksp;
918 */
Guojun Jin3deae8c2016-08-23 15:51:21 -0700919 node->sw_ksp_ptr = NULL;
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800920 node->task_offset = offsetof(struct thread_info, task);
921 node->pid_offset = offsetof(struct task_struct, tgid);
922}
923
924static struct proc_dir_entry *pdir;
925
926/*
927 * init_module cannot call exit_MODULE, so use this wrapper
928 */
929void netap_profile_release_resource(void)
930{
931 if (pdir) {
932 remove_proc_entry("rate", pdir);
933 remove_proc_entry("data", pdir);
934 remove_proc_entry("data1", pdir);
935 }
936 kfree(node[0]->ccl);
937 kfree(node[0]);
938 node[0] = NULL;
939}
940
941/*
942 * kernel module entry
943 */
944int __init netap_profile_init_module(void)
945{
Murat Sezgin3441e772015-10-26 11:55:57 -0700946#ifdef CONFIG_OF
947 /*
948 * If the node is not compatible, don't do anything.
949 */
950 if (!of_find_node_by_name(NULL, "nss-common")) {
951 return 0;
952 }
953#endif
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -0800954 /*
955 * we need N nodes, not one node + N ctx, for N cores
956 */
957 node[0] = kmalloc(sizeof(*node[0]) * NSS_MAX_CORES, GFP_KERNEL);
958 if (!node[0]) {
959 printk(KERN_INFO "Profiler CTRL kmalloc failed.\n");
960 return -ENOMEM;
961 }
962
963 node[0]->ccl = kmalloc(sizeof(*node[0]->ccl) * CCL_SIZE * NSS_MAX_CORES, GFP_KERNEL);
964 if (!node[0]->ccl) {
965 printk(KERN_INFO "Profiler n2h_sample_buf kmalloc failed.\n");
966 kfree(node[0]);
967 node[0] = NULL;
968 return -ENOMEM;
969 }
970
971 /*
972 * connect to the file system
973 */
974 pdir = proc_mkdir("profile", NULL);
975 if (!pdir ||
976 !proc_create("data", 0, pdir, &profile_fops) ||
977 !proc_create("data1", 0, pdir, &profile_fops) ||
978 !proc_create("rate", 0, pdir, &profile_rate_fops)) {
979 netap_profile_release_resource();
980 return -ENOMEM;
981 }
982
983 profile_init(node[0]);
984
985 /*
986 * attatch the device callback to N2H channel for CPU 0
987 */
988 node[0]->ctx = nss_profiler_notify_register(NSS_CORE_0, profile_handle_nss_data, node[0]);
989#if NSS_MAX_CORES > 1
990 node[1] = node[0] + 1;
991 node[1]->ccl = node[0]->ccl + CCL_SIZE;
992
993 profile_init(node[1]);
994 node[1]->ctx = nss_profiler_notify_register(NSS_CORE_1, profile_handle_nss_data, node[1]);
995 profile_register_performance_counter(&node[1]->profile_sequence_num, "Profile1 DRV data packets");
996#endif
997
998 profile_register_performance_counter(&node[0]->profile_sequence_num, "Profile0 DRV data packets");
999 return 0;
1000}
1001
1002/*
1003 * kernel module exit
1004 */
1005void __exit netap_profile_exit_module(void)
1006{
Murat Sezgin3441e772015-10-26 11:55:57 -07001007#ifdef CONFIG_OF
1008 /*
1009 * If the node is not compatible, don't do anything.
1010 */
1011 if (!of_find_node_by_name(NULL, "nss-common")) {
1012 return;
1013 }
1014#endif
Sundarajan Srinivasan1b03fe22014-12-02 13:20:56 -08001015 nss_profiler_notify_unregister(NSS_CORE_0);
1016#if NSS_MAX_CORES > 1
1017 nss_profiler_notify_unregister(NSS_CORE_1);
1018#endif
1019 netap_profile_release_resource();
1020}
1021
1022module_init(netap_profile_init_module);
1023module_exit(netap_profile_exit_module);
1024
1025MODULE_LICENSE("Dual BSD/GPL");