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