eal: bump ABI version for bus refactoring
[dpdk.git] / lib / librte_metrics / rte_metrics.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2017 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <string.h>
35 #include <sys/queue.h>
36
37 #include <rte_common.h>
38 #include <rte_malloc.h>
39 #include <rte_metrics.h>
40 #include <rte_lcore.h>
41 #include <rte_memzone.h>
42 #include <rte_spinlock.h>
43
44 #define RTE_METRICS_MAX_METRICS 256
45 #define RTE_METRICS_MEMZONE_NAME "RTE_METRICS"
46
47 /**
48  * Internal stats metadata and value entry.
49  *
50  * @internal
51  */
52 struct rte_metrics_meta_s {
53         /** Name of metric */
54         char name[RTE_METRICS_MAX_NAME_LEN];
55         /** Current value for metric */
56         uint64_t value[RTE_MAX_ETHPORTS];
57         /** Used for global metrics */
58         uint64_t global_value;
59         /** Index of next root element (zero for none) */
60         uint16_t idx_next_set;
61         /** Index of next metric in set (zero for none) */
62         uint16_t idx_next_stat;
63 };
64
65 /**
66  * Internal stats info structure.
67  *
68  * @internal
69  * Offsets into metadata are used instead of pointers because ASLR
70  * means that having the same physical addresses in different
71  * processes is not guaranteed.
72  */
73 struct rte_metrics_data_s {
74         /**   Index of last metadata entry with valid data.
75          * This value is not valid if cnt_stats is zero.
76          */
77         uint16_t idx_last_set;
78         /**   Number of metrics. */
79         uint16_t cnt_stats;
80         /** Metric data memory block. */
81         struct rte_metrics_meta_s metadata[RTE_METRICS_MAX_METRICS];
82         /** Metric data access lock */
83         rte_spinlock_t lock;
84 };
85
86 void
87 rte_metrics_init(int socket_id)
88 {
89         struct rte_metrics_data_s *stats;
90         const struct rte_memzone *memzone;
91
92         if (rte_eal_process_type() != RTE_PROC_PRIMARY)
93                 return;
94
95         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
96         if (memzone != NULL)
97                 return;
98         memzone = rte_memzone_reserve(RTE_METRICS_MEMZONE_NAME,
99                 sizeof(struct rte_metrics_data_s), socket_id, 0);
100         if (memzone == NULL)
101                 rte_exit(EXIT_FAILURE, "Unable to allocate stats memzone\n");
102         stats = memzone->addr;
103         memset(stats, 0, sizeof(struct rte_metrics_data_s));
104         rte_spinlock_init(&stats->lock);
105 }
106
107 int
108 rte_metrics_reg_name(const char *name)
109 {
110         const char * const list_names[] = {name};
111
112         return rte_metrics_reg_names(list_names, 1);
113 }
114
115 int
116 rte_metrics_reg_names(const char * const *names, uint16_t cnt_names)
117 {
118         struct rte_metrics_meta_s *entry;
119         struct rte_metrics_data_s *stats;
120         const struct rte_memzone *memzone;
121         uint16_t idx_name;
122         uint16_t idx_base;
123
124         /* Some sanity checks */
125         if (cnt_names < 1 || names == NULL)
126                 return -EINVAL;
127
128         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
129         if (memzone == NULL)
130                 return -EIO;
131         stats = memzone->addr;
132
133         if (stats->cnt_stats + cnt_names >= RTE_METRICS_MAX_METRICS)
134                 return -ENOMEM;
135
136         rte_spinlock_lock(&stats->lock);
137
138         /* Overwritten later if this is actually first set.. */
139         stats->metadata[stats->idx_last_set].idx_next_set = stats->cnt_stats;
140
141         stats->idx_last_set = idx_base = stats->cnt_stats;
142
143         for (idx_name = 0; idx_name < cnt_names; idx_name++) {
144                 entry = &stats->metadata[idx_name + stats->cnt_stats];
145                 strncpy(entry->name, names[idx_name],
146                         RTE_METRICS_MAX_NAME_LEN);
147                 memset(entry->value, 0, sizeof(entry->value));
148                 entry->idx_next_stat = idx_name + stats->cnt_stats + 1;
149         }
150         entry->idx_next_stat = 0;
151         entry->idx_next_set = 0;
152         stats->cnt_stats += cnt_names;
153
154         rte_spinlock_unlock(&stats->lock);
155
156         return idx_base;
157 }
158
159 int
160 rte_metrics_update_value(int port_id, uint16_t key, const uint64_t value)
161 {
162         return rte_metrics_update_values(port_id, key, &value, 1);
163 }
164
165 int
166 rte_metrics_update_values(int port_id,
167         uint16_t key,
168         const uint64_t *values,
169         uint32_t count)
170 {
171         struct rte_metrics_meta_s *entry;
172         struct rte_metrics_data_s *stats;
173         const struct rte_memzone *memzone;
174         uint16_t idx_metric;
175         uint16_t idx_value;
176         uint16_t cnt_setsize;
177
178         if (port_id != RTE_METRICS_GLOBAL &&
179                         (port_id < 0 || port_id > RTE_MAX_ETHPORTS))
180                 return -EINVAL;
181
182         if (values == NULL)
183                 return -EINVAL;
184
185         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
186         if (memzone == NULL)
187                 return -EIO;
188         stats = memzone->addr;
189
190         rte_spinlock_lock(&stats->lock);
191         idx_metric = key;
192         cnt_setsize = 1;
193         while (idx_metric < stats->cnt_stats) {
194                 entry = &stats->metadata[idx_metric];
195                 if (entry->idx_next_stat == 0)
196                         break;
197                 cnt_setsize++;
198                 idx_metric++;
199         }
200         /* Check update does not cross set border */
201         if (count > cnt_setsize) {
202                 rte_spinlock_unlock(&stats->lock);
203                 return -ERANGE;
204         }
205
206         if (port_id == RTE_METRICS_GLOBAL)
207                 for (idx_value = 0; idx_value < count; idx_value++) {
208                         idx_metric = key + idx_value;
209                         stats->metadata[idx_metric].global_value =
210                                 values[idx_value];
211                 }
212         else
213                 for (idx_value = 0; idx_value < count; idx_value++) {
214                         idx_metric = key + idx_value;
215                         stats->metadata[idx_metric].value[port_id] =
216                                 values[idx_value];
217                 }
218         rte_spinlock_unlock(&stats->lock);
219         return 0;
220 }
221
222 int
223 rte_metrics_get_names(struct rte_metric_name *names,
224         uint16_t capacity)
225 {
226         struct rte_metrics_data_s *stats;
227         const struct rte_memzone *memzone;
228         uint16_t idx_name;
229         int return_value;
230
231         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
232         /* If not allocated, fail silently */
233         if (memzone == NULL)
234                 return 0;
235
236         stats = memzone->addr;
237         rte_spinlock_lock(&stats->lock);
238         if (names != NULL) {
239                 if (capacity < stats->cnt_stats) {
240                         return_value = stats->cnt_stats;
241                         rte_spinlock_unlock(&stats->lock);
242                         return return_value;
243                 }
244                 for (idx_name = 0; idx_name < stats->cnt_stats; idx_name++)
245                         strncpy(names[idx_name].name,
246                                 stats->metadata[idx_name].name,
247                                 RTE_METRICS_MAX_NAME_LEN);
248         }
249         return_value = stats->cnt_stats;
250         rte_spinlock_unlock(&stats->lock);
251         return return_value;
252 }
253
254 int
255 rte_metrics_get_values(int port_id,
256         struct rte_metric_value *values,
257         uint16_t capacity)
258 {
259         struct rte_metrics_meta_s *entry;
260         struct rte_metrics_data_s *stats;
261         const struct rte_memzone *memzone;
262         uint16_t idx_name;
263         int return_value;
264
265         if (port_id != RTE_METRICS_GLOBAL &&
266                         (port_id < 0 || port_id > RTE_MAX_ETHPORTS))
267                 return -EINVAL;
268
269         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
270         /* If not allocated, fail silently */
271         if (memzone == NULL)
272                 return 0;
273         stats = memzone->addr;
274         rte_spinlock_lock(&stats->lock);
275
276         if (values != NULL) {
277                 if (capacity < stats->cnt_stats) {
278                         return_value = stats->cnt_stats;
279                         rte_spinlock_unlock(&stats->lock);
280                         return return_value;
281                 }
282                 if (port_id == RTE_METRICS_GLOBAL)
283                         for (idx_name = 0;
284                                         idx_name < stats->cnt_stats;
285                                         idx_name++) {
286                                 entry = &stats->metadata[idx_name];
287                                 values[idx_name].key = idx_name;
288                                 values[idx_name].value = entry->global_value;
289                         }
290                 else
291                         for (idx_name = 0;
292                                         idx_name < stats->cnt_stats;
293                                         idx_name++) {
294                                 entry = &stats->metadata[idx_name];
295                                 values[idx_name].key = idx_name;
296                                 values[idx_name].value = entry->value[port_id];
297                         }
298         }
299         return_value = stats->cnt_stats;
300         rte_spinlock_unlock(&stats->lock);
301         return return_value;
302 }