1 /*
2  * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
3  *
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
7  * copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /**
20  * DOC: qdf_perf
21  * This file provides OS dependent perf API's.
22  */
23 
24 #include <linux/version.h>
25 #include <linux/kernel.h>
26 #include <linux/uaccess.h>
27 #include <linux/fs.h>
28 #include <linux/proc_fs.h>
29 #include <linux/vmalloc.h>
30 #include <linux/list.h>
31 #include <linux/spinlock.h>
32 
33 #include <qdf_perf.h>
34 #include <qdf_module.h>
35 #ifdef QCA_PERF_PROFILING
36 
37 qdf_perf_entry_t     perf_root = {{0, 0} };
38 
39 /**
40  * qdf_perfmod_init() - Module init
41  *
42  * return: int
43  */
44 int
qdf_perfmod_init(void)45 qdf_perfmod_init(void)
46 {
47 	QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO,
48 		  "Perf Debug Module Init");
49 	INIT_LIST_HEAD(&perf_root.list);
50 	INIT_LIST_HEAD(&perf_root.child);
51 	perf_root.proc = proc_mkdir(PROCFS_PERF_DIRNAME, 0);
52 	return 0;
53 }
54 qdf_export_symbol(qdf_perfmod_init);
55 
56 /**
57  * qdf_perfmod_exit() - Module exit
58  *
59  * Return: none
60  */
61 void
qdf_perfmod_exit(void)62 qdf_perfmod_exit(void)
63 {
64 	QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO,
65 		  "Perf Debug Module Exit");
66 	remove_proc_entry(PROCFS_PERF_DIRNAME, 0);
67 }
68 qdf_export_symbol(qdf_perfmod_exit);
69 
70 /**
71  * __qdf_perf_init() - Create the perf entry
72  * @parent: parent perf id
73  * @id_name: name of perf id
74  * @type: type of perf counter
75  *
76  * return: perf id
77  */
78 qdf_perf_id_t
__qdf_perf_init(qdf_perf_id_t parent,uint8_t * id_name,qdf_perf_cntr_t type)79 __qdf_perf_init(qdf_perf_id_t parent, uint8_t *id_name,
80 		qdf_perf_cntr_t type)
81 {
82 	qdf_perf_entry_t    *entry  = NULL;
83 	qdf_perf_entry_t    *pentry = PERF_ENTRY(parent);
84 
85 	if (type >= CNTR_LAST) {
86 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
87 			  "%s:%s Invalid perf-type", __FILE__, __func__);
88 		goto done;
89 	}
90 
91 	if (!pentry)
92 		pentry = &perf_root;
93 	entry = kmalloc(sizeof(struct qdf_perf_entry), GFP_ATOMIC);
94 
95 	if (!entry) {
96 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
97 			  " Out of Memory,:%s", __func__);
98 		return NULL;
99 	}
100 
101 	memset(entry, 0, sizeof(struct qdf_perf_entry));
102 
103 	INIT_LIST_HEAD(&entry->list);
104 	INIT_LIST_HEAD(&entry->child);
105 
106 	spin_lock_init(&entry->lock_irq);
107 
108 	list_add_tail(&entry->list, &pentry->child);
109 
110 	entry->name = id_name;
111 	entry->type = type;
112 
113 	if (type == CNTR_GROUP) {
114 		entry->proc = proc_mkdir(id_name, pentry->proc);
115 		goto done;
116 	}
117 
118 	entry->parent   = pentry;
119 	entry->proc     = create_proc_entry(id_name, S_IFREG|S_IRUGO|S_IWUSR,
120 					pentry->proc);
121 	entry->proc->data       = entry;
122 	entry->proc->read_proc  = api_tbl[type].proc_read;
123 	entry->proc->write_proc = api_tbl[type].proc_write;
124 
125 	/*
126 	 * Initialize the Event with default values
127 	 */
128 	api_tbl[type].init(entry, api_tbl[type].def_val);
129 
130 done:
131 	return entry;
132 }
133 qdf_export_symbol(__qdf_perf_init);
134 
135 /**
136  * __qdf_perf_destroy - Destroy the perf entry
137  * @id: pointer to qdf_perf_id_t
138  *
139  * @return: bool
140  */
__qdf_perf_destroy(qdf_perf_id_t id)141 bool __qdf_perf_destroy(qdf_perf_id_t  id)
142 {
143 	qdf_perf_entry_t     *entry  = PERF_ENTRY(id),
144 		*parent = entry->parent;
145 
146 	if (!list_empty(&entry->child)) {
147 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
148 			  "Child's are alive, Can't delete");
149 		return A_FALSE;
150 	}
151 
152 	remove_proc_entry(entry->name, parent->proc);
153 
154 	list_del(&entry->list);
155 
156 	vfree(entry);
157 
158 	return true;
159 }
160 qdf_export_symbol(__qdf_perf_destroy);
161 
162 /**
163  * __qdf_perf_start - Start the sampling
164  * @id: Instance of qdf_perf_id_t
165  *
166  * Returns: none
167  */
__qdf_perf_start(qdf_perf_id_t id)168 void __qdf_perf_start(qdf_perf_id_t id)
169 {
170 	qdf_perf_entry_t *entry = PERF_ENTRY(id);
171 
172 	api_tbl[entry->type].sample(entry, 0);
173 }
174 qdf_export_symbol(__qdf_perf_start);
175 
176 /**
177  * __qdf_perf_end - Stop sampling
178  * @id: Instance of qdf_perf_id_t
179  *
180  * Returns: none
181  */
__qdf_perf_end(qdf_perf_id_t id)182 void __qdf_perf_end(qdf_perf_id_t id)
183 {
184 	qdf_perf_entry_t *entry = PERF_ENTRY(id);
185 
186 	api_tbl[entry->type].sample(entry, 1);
187 }
188 qdf_export_symbol(__qdf_perf_end);
189 
190 #endif /* QCA_PERF_PROFILING */
191