blob: dc87f6b87a7b93400b4070e297f0f4a904889fa2 [file] [log] [blame]
Guojun Jin20c98ad2014-05-02 14:08:48 -07001/*
2 **************************************************************************
3 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
4 * 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>
24#include <linux/export.h>
25#include <linux/module.h>
26#include <linux/seq_file.h>
27#include <linux/proc_fs.h>
28#include <linux/mm.h>
29#include <linux/mmzone.h>
30#include <linux/fs.h>
31#include <linux/page-flags.h>
32#include <linux/sched.h>
33#include <asm/uaccess.h>
34#include <asm/page.h>
35#include <asm/thread_info.h>
36
37#define NSS_PKT_STATS_ENABLED 0 // nss_core.h has no default DEF for NSS_PKT_STATS_ENABLED
38#include "nss_core.h" // needs only the number of NSS CORES
39
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. There are
49 * 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#ifndef PROFILE_DEBUG
76#define profileDebug(fmt, msg...)
77#define profileInfo(fmt, msg...)
78#define profileWarn(fmt, msg...)
79#else
80#define profileDebug(fmt, msg...) printk(KERN_DEBUG fmt, ##msg)
81#define profileInfo(fmt, msg...) printk(KERN_INFO fmt, ##msg)
82#define profileWarn(fmt, msg...) printk(KERN_WARNING fmt, ##msg)
83#endif
84
85/*
86 * LINUX and Ultra counters must all fit in one packet
87 */
88#define PROFILE_LINUX_MAX_COUNTERS 40
89static int profile_num_counters = 0;
90static volatile unsigned int *profile_counter[PROFILE_LINUX_MAX_COUNTERS];
91static char profile_name[PROFILE_LINUX_MAX_COUNTERS][PROFILE_COUNTER_NAME_LENGTH];
92
93/*
94 * internal function to check if @name has been registered before
95 * return the found index, or -1 otherwise
96 */
97static int __profile_find_entry(char *name)
98{
99 int i;
100
101 for (i = 0; i < profile_num_counters; i++) {
102 if (!strncasecmp(name, profile_name[i], PROFILE_COUNTER_NAME_LENGTH)) {
103 return i;
104 }
105 }
106 return -1;
107}
108
109/*
110 * profile_register_performance_counter - register @counter into profile tracking list by key @name
111 * @counter: pointer of the counter variable
112 * @name: identifier of this counter
113 *
114 * Returns zero if total entries exceeding PROFILE_LINUX_MAX_COUNTERS
115 * non-zero otherwise.
116 *
117 * Each @name gives unique entry for @counter, by allocating a new array slot or just use existing one.
118 * No need of de-registration API, since a loadable module's new insmod, will replace the
119 * @counter's * new address at the same profile_counter[] slot.
120 */
121static int profile_register_performance_counter(volatile unsigned int *counter, char *name)
122{
123 int i;
124
125 if (profile_num_counters >= PROFILE_LINUX_MAX_COUNTERS) {
126 return 0;
127 }
128
129 i = __profile_find_entry(name);
130 if (i < 0) {
131 i = profile_num_counters++;
132 }
133
134 profile_counter[i] = counter;
135 strncpy(profile_name[i], name, PROFILE_COUNTER_NAME_LENGTH);
136 profile_name[i][PROFILE_COUNTER_NAME_LENGTH - 1] = 0;
137
138 return 1;
139}
140
141/*
142 * make a packet full of sample data
143 */
144static int profile_make_data_packet(char *buf, int blen, struct profile_io *pn)
145{
146 int ns; /* number of samples requested */
147 struct profile_header ph;
148 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
149
150 if (blen < sizeof(ph) + sizeof(struct profile_sample)) {
151 return -EINVAL;
152 }
153
154 profileDebug("%p stat %x cnt %d %p\n", pn->pnc.pn2h, pn->pnc.pn2h->mh.md_type, psc_hd->count, pn->ccl);
155
156 if (pn->pnc.pn2h->mh.md_type == PINGPONG_EMPTY || psc_hd->count < 1) {
157 struct profile_n2h_sample_buf *nsb;
158 ns = (pn->ccl_read + 1) & (CCL_SIZE-1);
159 nsb = pn->ccl + ns;
160 if (ns == pn->ccl_write || nsb->mh.md_type != PINGPONG_FULL) {
161 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);
162 return -EAGAIN;
163 }
164 pn->ccl_read = ns;
165 profileInfo("sp %p => %p rd %d %p\n", pn->pnc.samples, nsb->samples, ns, nsb);
166 psc_hd = &nsb->psc_header;
167 pn->pnc.pn2h = nsb;
168 pn->pnc.samples = nsb->samples;
169 pn->pnc.cur = 0;
170 }
171 pn->pnc.pn2h->mh.md_type = PINGPONG_INUSE;
172
173 /*
174 * fill in the packet header
175 */
176 memset(&ph, 0, sizeof(ph));
177 ph.pph.magic = htons(PROF_MAGIC + PROFILE_VERSION);
178 ph.pph.header_size = sizeof(ph);
179 ph.pph.profile_instructions = 0;
180 ph.pph.clock_freq = pn->pnc.un.cpu_freq;
181 ph.pph.ddr_freq = pn->pnc.un.ddr_freq;
182 ph.pph.cpu_id = pn->pnc.un.cpu_id;
183 ph.pph.seq_num = htonl(pn->profile_sequence_num);
184 ph.pph.sample_stack_words = htonl(PROFILE_STACK_WORDS);
185
186 ns = (blen - sizeof(ph)) / sizeof(struct profile_sample);
187 profileInfo("%X: ns = %d psc_hd count %d phs %d pss %d\n", pn->profile_sequence_num, ns, psc_hd->count, sizeof(ph), sizeof(struct profile_sample));
188 if (ns > psc_hd->count)
189 ns = psc_hd->count;
190 if (ns == 0) {
191 printk("NS should not be 0: rlen %d hd cnt %d\n", blen, psc_hd->count);
192 return 0;
193 }
194 ph.pph.sample_count = ns;
195 if (copy_to_user(buf, &ph.pph, sizeof(ph.pph)) != 0) {
196 return -EFAULT;
197 }
198 buf += sizeof(ph.pph);
199
200 /*
201 * ph.exh is unused dummy; and psc_hd->exh is used directly to avoid double mem copy
202 */
203 if (copy_to_user(buf, &psc_hd->exh, sizeof(psc_hd->exh)) != 0) {
204 return -EFAULT;
205 }
206 buf += sizeof(psc_hd->exh);
207
208 blen = ns * sizeof(struct profile_sample);
209 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));
210 if (copy_to_user(buf, &pn->pnc.samples[pn->pnc.cur], blen) != 0) {
211 return -EFAULT;
212 }
213 pn->pnc.cur += ns;
214 psc_hd->count -= ns;
215 if (psc_hd->count < 1)
216 pn->pnc.pn2h->mh.md_type = PINGPONG_EMPTY;
217
218 pn->profile_sequence_num++;
219 blen += sizeof(ph);
220 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);
221 return blen;
222}
223
224/*
225 * This is no longer needed due to NetAP and Linux use different CPUs, and profile is NetAP only.
226 * All related code will be removed after corresponging code in visual tool is corrected; otherwise
227 * visual tool will mis-behave
228 */
229struct profile_counter profile_builtin_stats[] =
230{
231 {
232 "Free memory(KB)", 0
233 },
234 {
235 "Max free Block(KB)", 0
236 }
237};
238
239/*
240 * make a packet full of performance counters
241 */
242static int profile_make_stats_packet(char *buf, int bytes, struct profile_io *pn)
243{
244 static char prof_pkt[PROFILE_MAX_PACKET_SIZE];
245
246 char *ptr;
247 int stat_count;
248 int n;
249 struct profile_counter counter;
250 struct profile_header_counters *hdr = (struct profile_header_counters *)prof_pkt;
251 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
252
253 if (bytes > PROFILE_MAX_PACKET_SIZE) {
254 bytes = PROFILE_MAX_PACKET_SIZE;
255 }
256 hdr->linux_count = sizeof(profile_builtin_stats) / sizeof(counter);
257 stat_count = (bytes - sizeof(hdr)) / sizeof (counter);
258 stat_count -= hdr->linux_count;
259
260 if (stat_count <= 0) {
261 return 0;
262 }
263
264 if (stat_count > pn->pnc.un.num_counters + profile_num_counters) {
265 stat_count = pn->pnc.un.num_counters + profile_num_counters;
266 }
267
268 hdr->magic = htons(PROF_MAGIC_COUNTERS);
269 hdr->ultra_count = htons(stat_count);
270 hdr->linux_count = htonl(hdr->linux_count);
271 hdr->ultra_sample_time = psc_hd->exh.clocks;
272 hdr->linux_sample_time = 0;
273
274 n = stat_count;
275 if (n > pn->pnc.un.num_counters) // copy NSS counters
276 n = pn->pnc.un.num_counters;
277 n *= sizeof(pn->pnc.un.counters[0]);
278 ptr = (char*) (hdr + 1);
279 memcpy(ptr, (void *)(pn->pnc.un.counters), n);
280 ptr += n;
281
282 for (n = 0; n < profile_num_counters && n + pn->pnc.un.num_counters < stat_count; ++n) {
283 counter.value = *(profile_counter[n]);
284 strcpy(counter.name, profile_name[n]);
285 memcpy(ptr, (void *)(&counter), sizeof(counter));
286 ptr += sizeof(counter);
287 }
288
289 /*
290 * built in statistics
291 profile_get_memory_stats(&total_free, &max_free);
292 */
293 profile_builtin_stats[0].value = 0;
294 profile_builtin_stats[1].value = 0;
295 memcpy(ptr, (void *)profile_builtin_stats, sizeof(profile_builtin_stats));
296 ptr += sizeof(profile_builtin_stats);
297
298 n = ptr - prof_pkt;
299 if (copy_to_user(buf, prof_pkt, n) != 0) {
300 return -EFAULT;
301 }
302 return n;
303}
304
305/*
306 * space for all memory blocks so we can hold locks for short time when walking tables
307 */
308static struct profile_io *node[NSS_MAX_CORES];
309
310static int profile_open(struct inode *inode, struct file *filp)
311{
312 int n;
313 struct profile_io *pn;
314
315 if (filp->private_data)
316 printk(KERN_WARNING "%s: %p\n", filp->f_dentry->d_iname, filp->private_data);
317
318 n = filp->f_dentry->d_iname[strlen(filp->f_dentry->d_iname) - 1] - '0';
319 if (n < 0 || n >= NSS_MAX_CORES)
320 n = 0;
321 pn = node[n];
322 if (!pn) {
323 return -ENOENT;
324 }
325
326 if (!pn->pnc.enabled && nss_get_state(pn->ctx) == NSS_STATE_INITIALIZED) {
327 nss_tx_status_t ret;
328 pn->pnc.enabled = 1;
329 pn->profile_first_packet = 1;
330 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | PROFILER_START;
331 ret = nss_tx_profiler_if_buf(pn->ctx, (uint8_t *)&pn->pnc.un, sizeof(pn->pnc.un));
332 profileInfo("%s: %d -- %p: ccl %p sp %p\n", __func__, ret, pn, pn->ccl, pn->pnc.samples);
333 filp->private_data = pn;
334 return 0;
335 }
336
337 profileWarn("profile ena %d nss stat %x\n", pn->pnc.enabled, nss_get_state(pn->ctx));
338 return -EBUSY;
339}
340
341/*
342 * return a udp packet ready to send to the profiler tool
343 * when there are no packets left to make, return 0
344 */
345static int profile_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
346{
347 int result = 0;
348 struct profile_io *pn = (struct profile_io *)filp->private_data;
349 if (!pn) {
350 return -ENOENT;
351 }
352
353 if (!pn->pnc.enabled) {
354 return -EPERM;
355 }
356 if (!pn->pnc.samples) {
357 return -ENOMEM;
358 }
359
360 if (pn->profile_first_packet) {
361 result = profile_make_stats_packet(buf, count, pn);
362 pn->profile_first_packet = 0;
363 profileInfo("%d profile_make_stats_packet %d\n", result, count);
364 }
365
366 if (result == 0) {
367 result = profile_make_data_packet(buf, count, pn);
368 if (result == 0) {
369 pn->profile_first_packet = 1;
370 }
371 profileInfo("%d: profile_make_data_packet %d\n", result, count);
372 }
373
374 profileInfo("%d: read %d\n", pn->pnc.enabled, result);
375 if (pn->pnc.enabled < 0) {
376 nss_tx_status_t ret;
377 pn->pnc.enabled = 1;
378 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | PROFILER_START;
379 ret = nss_tx_profiler_if_buf(pn->ctx, (uint8_t *)&pn->pnc.un, sizeof(pn->pnc.un));
380 profileWarn("%s: restart %d -- %p: ccl %p sp %p\n", __func__, ret, pn, pn->ccl, pn->pnc.samples);
381 }
382
383 return result;
384
385}
386
387/*
388 * the close function paired with profiler_open
389 */
390static int profile_release(struct inode *inode, struct file *filp)
391{
392 struct profile_io *pn = (struct profile_io *)filp->private_data;
393 if (!pn) {
394 return -ENOENT;
395 }
396
397 if (pn->pnc.enabled) {
398 nss_tx_status_t ret;
399 pn->pnc.enabled = 0;
400 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | PROFILER_STOP;
401 ret = nss_tx_profiler_if_buf(pn->ctx, (uint8_t *)&pn->pnc.un, sizeof(pn->pnc.un));
402 profileInfo("%s: %p %d\n", __func__, pn, ret);
403 return 0;
404 }
405 profileWarn("%s: attempt closing non-open dev %p\n", __func__, pn);
406 pn->profile_first_packet = 1;
407 return -EBADF;
408}
409
410#define isspace(c) (c==' ' || c=='\t')
411
412static int parseDbgData(const char *buf, size_t count, struct debug_box *db)
413{
414 char *cp;
415 int n;
416
417 printk("%p: buf (%s) cnt %d\n", buf, buf, count);
418 if (sscanf(buf, "%x", (uint32_t *)&db->base_addr) != 1) {
419 printk("%s: cannot get base addr\n", __func__);
420 return -EINVAL;
421 }
422
423 cp = strchr(buf, ' ');
424 if (!cp) {
425noea: printk("%s: no enough arguments\n", __func__);
426 return -EFAULT;
427 }
428
429 while (isspace(*cp)) cp++;
430 if (!strncmp(cp, "mio", 3) || !strncmp(cp, "moveio", 6)) {
431 printk("%p: cp (%s)\n", cp, cp);
432 cp = strchr(cp, ' ');
433 if (!cp) {
434 goto noea;
435 }
436 db->opts |= DEBUG_OPT_MOVEIO;
437 }
438
439 while (isspace(*cp)) cp++;
440 printk("base addr %p -- %s\n", db->base_addr, cp);
441
442 if (!strncmp(cp, "read", 4)) {
443 cp = strchr(cp, ' ');
444 if (cp) {
445 while (isspace(*cp)) cp++;
446 sscanf(cp, "%x", &db->dlen);
447 }
448 return 0;
449 }
450
451 n = 0;
452 do {
453 while (isspace(*cp)) cp++;
454 if (sscanf(cp, "%x", db->data+n) != 1) {
455 printk("n %d : %s\n", n, cp);
456 break;
457 }
458 printk("write %x to off %x\n", db->data[n], n * sizeof(db->data[0]));
459 n++;
460 cp = strchr(cp, ' ');
461 } while (cp && n < MAX_DB_WR);
462 return n;
463}
464
465/*
466 * display memory content read from Phy addr
467 */
468static void debug_if_show(struct debug_box *db, int buf_len)
469{
470 int i;
471
472 for (i=0; i < db->dlen; i++) {
473 if ((i & 3) == 0)
474 printk("\n%p: ", db->base_addr + i);
475 printk("%9x", db->data[i]);
476 }
477 printk("\ndumped %d (extra 1) blen %d\n", db->dlen, buf_len);
478}
479
480/*
481 * a generic Krait <--> NSS debug interface
482 */
483static int debug_if(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
484{
485 int result;
486 struct debug_box *db;
487 struct profile_io *pio = node[0];
488
489 if (!pio) {
490 return -ENOENT;
491 }
492
493 if (!pio->pnc.enabled) {
494 return -EPERM;
495 }
496
497 db = (struct debug_box *) &pio->pnc;
498 db->dlen = db->opts = 0;
499 result = parseDbgData(buf, count, db);
500 if (result < 0) {
501 return result;
502 }
503
504 if (!result) {
505 db->hd_magic = UBI32_PROFILE_HD_MAGIC | DEBUG_RD_REQ;
506 } else {
507 db->hd_magic = UBI32_PROFILE_HD_MAGIC | DEBUG_WR_REQ;
508 db->dlen = result;
509 }
510 result = nss_tx_profiler_if_buf(pio->ctx, (uint8_t *)&pio->pnc.un, sizeof(pio->pnc.un));
511 printk("dbg res %d dlen = %d opt %x\n", result, db->dlen, db->opts);
512 return count;
513}
514
515static const struct file_operations profile_fops = {
516 .open = profile_open,
517 .read = profile_read,
518 .release = profile_release,
519 .write = debug_if,
520};
521
522/*
523 * showing sample status on Linux console
524 */
525static int profile_rate_show(struct seq_file *m, void *v)
526{
527 struct profile_io *pn = node[0];
528 if (pn) {
529 struct profile_sample_ctrl_header *psc_hd = &pn->pnc.pn2h->psc_header;
530 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",
531 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);
532 } else {
533 seq_printf(m, "Profiler is not initialized.\n");
534 }
535 return 0;
536}
537
538static int profile_rate_open(struct inode *inode, struct file *filp)
539{
540 return single_open(filp, profile_rate_show, NULL);
541}
542
543static int profile_rate_write(struct file *filp, const char *buf, size_t len, loff_t *off)
544{
545 *off = 0;
546 return 0;
547}
548
549static const struct file_operations profile_rate_fops = {
550 .open = profile_rate_open,
551 .read = seq_read,
552 .llseek = seq_lseek,
553 .release = single_release,
554 .write = profile_rate_write,
555};
556
557static void kxdump(void *buf, int len, const char *who)
558{
559 int *ip = (int*) buf;
560 int lns = len >> 5; // 32-B each line
561 if (lns > 4)
562 lns = 4;
563 printk("%p: kxdump %s: len %d\n", buf, who, len);
564 do {
565 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]);
566 ip += 8;
567 } while (lns--);
568}
569
570static void profile_handle_nss_data(void *arg, uint8_t *buf, uint16_t buf_len)
571{
572 struct profile_io *pn;
573 struct profile_n2h_sample_buf *nsb;
574 struct profile_sample_ctrl_header *psc_hd = (struct profile_sample_ctrl_header *)buf;
575 int cmd, wr;
576 int swap = 0; // only for header and info data, not samples
577
578 if (buf_len < (sizeof(struct profile_session) - sizeof(struct profile_counter) * (PROFILE_MAX_APP_COUNTERS - 1))) {
579 printk("profile data packet is too small to be useful %d\n", buf_len);
580 return;
581 }
582 if ((psc_hd->hd_magic & UBI32_PROFILE_HD_MMASK) != UBI32_PROFILE_HD_MAGIC) {
583 if ((psc_hd->hd_magic & UBI32_PROFILE_HD_MMASK_REV) != UBI32_PROFILE_HD_MAGIC_REV) {
584 kxdump(buf, buf_len, "bad profile packet");
585 printk("bad profile packet %x : %d\n", psc_hd->hd_magic, buf_len);
586 return;
587 }
588 profileDebug("Profile data in different Endian type %x\n", psc_hd->hd_magic);
589 swap = 1;
590 psc_hd->hd_magic = ntohl(psc_hd->hd_magic);
591 }
592 cmd = psc_hd->hd_magic & ~UBI32_PROFILE_HD_MMASK;
593
594 pn = (struct profile_io *)arg;
595 profileInfo("%s: dlen %d swap %d cmd %x - %d\n", __func__, buf_len, swap, cmd, (pn->ccl_read - pn->ccl_write) & (CCL_SIZE-1));
596 //kxdump(buf, buf_len, "process profile packet");
597 if (cmd == PROFILER_FIXED_INFO) {
598 struct profile_session *pTx = (struct profile_session *)buf;
599 if (swap) {
600 pn->pnc.un.rate = ntohl(pTx->rate);
601 pn->pnc.un.cpu_id = ntohl(pTx->cpu_id);
602 pn->pnc.un.num_counters = ntohl(pTx->num_counters);
603 } else {
604 pn->pnc.un = *pTx;
605 }
606 return;
607 }
608
609 if (cmd == DEBUG_REPLY) {
610 debug_if_show((struct debug_box*) buf, buf_len);
611 return;
612 }
613
614 wr = (pn->ccl_write + 1) & (CCL_SIZE-1);
615 nsb = pn->ccl + wr;
616 swap = (pn->ccl_read - wr) & (CCL_SIZE-1); // PROFILER_FLOWCTRL
617 if (nsb->mh.md_type != PINGPONG_EMPTY || (swap && swap < 5)) {
618 if (pn->pnc.enabled > 0) {
619 pn->pnc.enabled = -1;
620 pn->pnc.un.hd_magic = UBI32_PROFILE_HD_MAGIC | PROFILER_STOP;
621 cmd = nss_tx_profiler_if_buf(pn->ctx, (uint8_t *)&pn->pnc.un, sizeof(pn->pnc.un));
622 profileWarn("temp stop sampling engine %d\n", cmd);
623 }
624 if (swap < 3) {
625 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);
626 return; // -EMSGSIZE
627 }
628 }
629 pn->ccl_write = wr;
630
631 /*
632 * smapling data -- hdr NBO swap is done at NSS side via SWAPB.
633 */
634 memcpy(&nsb->psc_header, buf, buf_len); // pn->pnc.pn2h->psc_header = *psc_hd; maybe faster, but take more memory
635
636 nsb->mh.md_type = PINGPONG_FULL;
637 //kxdump((void*)(nsb->samples + 23), sizeof(*nsb->samples) << 1, "1st 2 samples");
638 profileInfo("filled %p %p wr %d\n", nsb, nsb->samples, pn->ccl_write);
639}
640
641/*
642 * initialize basic profile data structure
643 */
644static void profile_init(struct profile_io *node)
645{
646 int n;
647
648 memset(&node->pnc, 0, sizeof(node->pnc));
649 node->ccl_read = 0;
650 node->ccl_write = -1;
651 node->pnc.pn2h = node->ccl;
652 node->pnc.samples = node->ccl->samples;
653
654 for (n=0; n<CCL_SIZE; n++) {
655 node->ccl[n].mh.md_type = PINGPONG_EMPTY;
656 node->ccl[n].psc_header.count = 0;
657 }
658
659 /*
660 * sw_ksp is an array of pointers to struct thread_info, the current task executing for each linux virtual processor
661 node->sw_ksp_ptr = sw_ksp;
662 */
663 node->task_offset = offsetof(struct thread_info, task);
664 node->pid_offset = offsetof(struct task_struct, tgid);
665}
666
667static struct proc_dir_entry *pdir;
668
669/*
670 * init_module cannot call exit_MODULE, so use this wrapper
671 */
672void netap_profile_release_resource(void)
673{
674 if (pdir) {
675 remove_proc_entry("rate", pdir);
676 remove_proc_entry("data", pdir);
677 remove_proc_entry("data1", pdir);
678 }
679 kfree(node[0]->ccl);
680 kfree(node[0]);
681 node[0] = NULL;
682}
683
684/*
685 * kernel module entry
686 */
687int __init netap_profile_init_module(void)
688{
689 /*
690 * we need N nodes, not one node + N ctx, for N cores
691 */
692 node[0] = kmalloc(sizeof(*node) * NSS_MAX_CORES, GFP_KERNEL);
693 if (!node[0]) {
694 printk(KERN_INFO "Profiler CTRL kmalloc failed.\n");
695 return -ENOMEM;
696 }
697
698 node[0]->ccl = kmalloc(sizeof(*node[0]->ccl) * CCL_SIZE * NSS_MAX_CORES, GFP_KERNEL);
699 if (!node[0]->ccl) {
700 printk(KERN_INFO "Profiler n2h_sample_buf kmalloc failed.\n");
701 kfree(node[0]);
702 node[0] = NULL;
703 return -ENOMEM;
704 }
705
706 node[1] = node[0] + 1;
707 node[1]->ccl = node[0]->ccl + 1;
708
709 profile_init(node[0]);
710 profile_init(node[1]);
711
712 /*
713 * attatch the device callback to N2H channel for CPU 0
714 */
715 node[0]->ctx = nss_register_profiler_if(profile_handle_nss_data, NSS_CORE_0, node[0]);
716 node[1]->ctx = nss_register_profiler_if(profile_handle_nss_data, NSS_CORE_1, node[1]);
717
718
719 /*
720 * connect to the file system
721 */
722 pdir = proc_mkdir("profile", NULL);
723 if (!pdir ||
724 !proc_create("data", 0, pdir, &profile_fops) ||
725 !proc_create("data1", 0, pdir, &profile_fops) ||
726 !proc_create("rate", 0, pdir, &profile_rate_fops)) {
727 netap_profile_release_resource();
728 return -ENOMEM;
729 }
730
731 profile_register_performance_counter(&node[0]->profile_sequence_num, "Profile0 driver data packets");
732 profile_register_performance_counter(&node[1]->profile_sequence_num, "Profile1 driver data packets");
733 return 0;
734}
735
736/*
737 * kernel module exit
738 */
739void __exit netap_profile_exit_module(void)
740{
741 netap_profile_release_resource();
742}
743
744module_init(netap_profile_init_module);
745module_exit(netap_profile_exit_module);
746
747MODULE_LICENSE("Dual BSD/GPL");